Pero para tus webapps de uso personal no es necesario perder el tiempo diseñando una imagen, ni tampoco dejar el cutre icono por defecto del navegador.
Puedes usar un Emoji, que le dará un toque más personal a tu página, tal y como nos indica Lea Verou en este tweet:
Now that all modern browsers support SVG favicons, here’s how to turn any emoji into a favicon.svg:
Si conoces mis artículos de #WarTraces, verás que utilizo imágenes satélite de la Agencia Espacial Europea. Hoy os cuento cómo descargar y manipular estas imágenes de la misión Sentinel 2, así como de Landsat, ya que el tratamiento es similar para ambos.
Lo haremos con QGIS, para lo cual deberás descargar e instalar este programa con el módulo de GRASS GIS incluido.
Contenido
Registro en plataformas.
Necesitaremos crearnos una cuenta para poder acceder a ambas fuentes de datos, rellenando un formulario y confirmando la cuenta por correo electrónico.
Ahora, dibujaremos el polígono delimitador de la zona de interés. Cada punto del rectángulo se marca haciendo click derecho, y cerramos el polígono con doble click derecho.
A continuación, pulsamos el botón de filtro de la barra de búsqueda y seleccionamos la casilla de la misión Sentinel-2 (también podremos delimitar las fechas de la consulta, pero por defecto se ordenará de más reciente a más antiguo).
Por último, aparecerán los productos disponibles para nuestra consulta, los cuales podremos previsualizar o descargar en un fichero .zip comprimido.
NASA Earthdata Search
Igual que antes, deberemos loguearnos primero en su visor de datos.
Podremos definir también una zona de interés mediante las herramientas de geometría de la barra lateral.
Filtramos los productos, por ejemplo seleccionando "Imagery", y seleccionamos HLS Landsat en el catálogo.
En este caso, deberemos seleccionar las bandas que queramos descargar y que veremos a continuación.
Imagen TCI (true colour o color verdadero)
Simplificando (mucho), los satélites capturan imágenes en distintos rangos de longitudes de onda, denominados bandas.
Además, las imágenes o fotografías digitales que usamos para representar la realidad como las ve el ojo humano suelen estar definidas por tres capas de datos: una para el color rojo (Red), otra para el color verde (Green), y otra para el azul (Blue), definiendo en cada una de estas capas un valor para cada pixel con la cantidad de ese color. La combinación de todos los píxeles es lo que simula una imagen similar a la realidad.
TIP! Haz la prueba y haz mucho zoom a cualquier imagen y verás el mosaico de píxeles que la forma.
Utilizaremos las bandas capturadas por los satélites dentro de la longitud de onda visible para generar una imagen tal y como la veríamos desde el espacio.
Sentinel 2
En el caso de Sentinel 2, podemos mostrar directamente una imagen de color verdadero descargando el producto L1C y empleando la capa _TCI.jp2 ubicada en la ruta /GRANULE/DATA_CODE_NAME/IMG_DATA/
La imagen TCI se verá de la siguiente forma:
Deir ez Zor, Siria, 22/10/2017. TCI. Fuente: Copernicus Sentinel 2.
Para Landsat, deberemos combinar las diferentes bandas capturadas individualmente por el satélite dentro de la longitud de onda visible. Estas son:
RED
B04
GREEN
B03
BLUE
B02
Acudimos a QGIS, y usamos la función i.group del módulo GRASS que combinará las capas seleccionadas en una nueva capa multibanda RGB.
Para facilitar la tarea, yo renombro las capas para ordenarlas de la siguiente manera, asegurándote que el script las lee correctamente:
Quedando así:
En este caso no se parece nada a la imagen anterior o a la realidad, así que ajustamos las propiedades para extraer más color de los datos:
En primer lugar nos aseguramos de leer toda la información de las bandas usando los valores mínimo y máximo reales, en lugar de los estimados.
Solo con esto mejora bastante la imagen, pero le falta algo de brillo y de color.
Ajustamos ahora los parámetros de renderizado de color:
Y ya queda algo mucho más realista:
Deir ez Zor, Siria, 21/10/2017. TCI. Fuente: Landsat.
Capa SWIR (short wave infra-red)
Utilizando las bandas de longitud de onda infra-roja, podemos ver más allá de lo que ve el ojo humano, o incluso a través de ciertos objetos, como es el caso de ciertas nubes o el humo.
Durante los incendios del verano, vi cómo Copernicus utilizaba estas imágenes para delimitar la extensión del fuego, y pensé que servirían para localizar el impacto de las bombas que oculta el humo que desprenden. Y así fue cómo se me ocurrió utilizarlo para #WarTraces.
Sentinel 2
Usamos el procedimiento anterior, combinando para este caso las siguientes bandas:
RED
B12
GREEN
B8A
BLUE
B04
Deir ez Zor, Siria, 22/10/2017. SWIR. Fuente: Copernicus Sentinel 2.
Landsat
Realizamos una nueva combinación, en este caso usando las bandas siguientes:
RED
B07
GREEN
B06
BLUE
B04
Deir ez Zor, Siria, 21/10/2017. SWIR. Fuente: Landsat.
Conclusión
Como nota final, es importante destacar el tamaño de pixel, ya que afectará al detalle de la imagen (resolución):
Sentinel 2: 10 metros.
Landsat: 30 metros.
Por otro lado, Landsat cuenta con imágenes desde 2013, mientras que Sentinel solo dispone de imágenes desde 2015.
Además, puedes previsualizar todas estas capas y combinaciones con visores web como EO Browser de Sentinel Hub.
Bombardeos al Aeropuerto Internacional Antonov en Kyiv, Ucrania, el 26/2/2022, usando EO Browser.
Si te atascas o quieres comentar, te espero en 🐦 Twitter!
Blender es (para mí), el programa de modelado 3D gratuito por excelencia. Tiene una amplísima comunidad, muchísima documentación, tutoriales y, sobre todo, continuas actualizaciones y mejoras.
Uno de sus plugins más útiles es BlenderGIS, con el que podremos volcar datos geográficos, georreferenciados o no, para poder modelar con ellos.
Vamos a ver un ejemplo de uso para un modelo de elevaciones.
Contenido
Instalación
Lo primero que debemos hacer es descargar e instalar Blender desde su fuente oficial (su página web) o desde la tienda de aplicaciones de nuestro sistema operativo:
A continuación ejecutamos Blender y abrimos los ajustes de Add-ons (Edit > Preferences > Add-ons).
Pulsamos en "Install..." y seleccionamos el fichero .zip de BlenderGIS.
Ahora podremos buscarlo y activarlo.
Verás que ahora aparece el menú "GIS" en la barra superior de Blender.
Descargar información geográfica
En este ejemplo yo voy a usar un Modelo Digital del Terreno en formato ASCII (.asc), ya que es uno de los formatos de trabajo de BlenderGIS y también el formato estándar de descarga.
Si la información la descargas en otro formato como .tiff o .xyz, lo podrás convertir usando algún programa de escritorio como QGIS o ArcGIS.
MDT
En mi caso, usaré el MDT200 del IGN español, un modelo con paso de malla (o tamaño de celdas) de 200 metros, y es que quiero representar una zona bastante amplia que incluye la provincia de Álava.
Además, podemos usar una ortofoto como textura del terreno. Para ello me ayudaré de QGIS, y cargaré el servicio WMS del PNOA (también del IGN) para recortar la imagen satélite al gusto.
Cargada la ortofoto, la exportaremos como imagen renderizada para la extensión de nuestro MDT, y con tamaño de celda de 20 metros (la ortofoto admite hasta unos 20cm de tamaño de celda, pero el archivo sería ya excesivamente grande, siendo la imagen de 20 metros de 140MB).
TIP! Una forma de optimizar el detalle es generar "teselas", o una cuadrícula de menor tamaño, pero mayor resolución.
Modelado en Blender
Así estaría todo listo para trabajar en Blender.
Usando el menú "GIS", importamos la capa como malla ASCII. Verás que en seguida aparecerá el modelo en pantalla.
TIP! Este modelo está centrado en el origen de coordenadas, pero se podría georeferenciar estableciendo su CRS en las propiedades de "Georeference".
Ahora añadimos la textura satélite:
Creamos un nuevo material.
Creamos una nueva textura y cargamos la imagen satélite.
Ahora nos movemos a la pestaña de UV Editing:
Selecciona la capa de terreno en la ventana derecha, entra en Edit Mode, y "Seleccionar todos" los polígonos (Ctrl+A). Deberías verlo naranja como abajo y asegúrate de estar en la vista "Supertior" (pulsa el número 7).
Despliega las herramientas "UV" del menú superior y proyecta la capa del terreno con "Project from View (bounds)". Esto hará que se ajuste a la extensión de la imagen.
Elegimos la imagen de la textura para aplicarle la proyección y vemos que se ajustan las celdas del modelo a la imagen (haz un poco de zoom para comprobarlo).
Por último, vamos a la pestaña de Shading y añadimos el elemento "Image Texture" seleccionando la imagen de la textura y conectamos el Vector al UV y el Color al Shader (copiar la imagen).
Ahora, si volvemos a la ventana de Layout, nuestro modelo mostrará la imagen satélite perfectamente ajustada.
Y ya estaría listo, con esto puedes ahora editar y exportar tu modelo, por ejemplo, para imprimirlo en 3D o usarlo en Unity3D.
Si quieres comprobar el seguimiento que hace de tí una web haz este sencillo experimento.
Entra en el inspector del navegador con Crtl+I o click derecho «Inspeccionar»
Abre la pestaña de «Red».
Ahora ve a la página principal del blog.
Esto es lo que verás: apenas 8 archivos que cargan todo lo necesario para que navegues por el blog.
Ahora abre cualquier otra página que suelas visitar y observa la misma pestaña de red.
Yo voy a entrar en amazon.es y no voy a entrar en detalles. Solo fíjate que se irá generando un nuevo fichero /1/batch/1/OE al final cada poco tiempo. Ahora mueve despacio el ratón sin bajar la ventana. Verás que el contenido de esos ficheros contiene una variable t de tiempo y unas coordenadas x e y que indican donde tienes el cursor en cada momento. Esto es solo un ejemplo del tracking que se realiza habitualmente: desde dónde entraste a la web, qué clickas, qué ves, donde te detienes, tu IP, tu geolocalización… Aquí no hay nada de eso.
Esto en cuanto a Amazon y su AWS. Los ficheros de rastreo de Google son los famosos «analytics» o «collects» que también verás en la pestaña de Red en las páginas que lo utilicen (que son la mayoría).
Se suele decir que la mejor solución es la más sencilla, y de hecho, como ves en este blog, no necesitas tantas florituras para publicar una web. Así que desconfía de sitios tediosos, con un interminable listado de «partners» y con servicios de Google Analytics o Amazon Web Services.
Una de las formas de evitar el uso de botones aparatosos en tus proyectos Arduino es utilizar placas o módulos con pines táctiles, como algunos módulos de ESP32.
Eso sí, la mayoría de placas no cuentan con esta característica, pero leyendo esta entrada en el foro de esp32.com di con algo interesante:
Con una rápida calibración, se puede usar cualquier pin analógico como sensor táctil. Aunque hay varias librerías al respecto, realmente solo necesitas un par de funciones, así que vamos al grano!
Contenido
Componentes
Para este proyecto voy a usar los siguientes compontentes:
Verás que indica "ProMicro", pero realmente es compatible con cualquier microcontrolador. Solo tienes que elegir tu placa en el menú de Herramientas.
La Pro Micro lleva un controlador ATMega32u4, que es compatible con la placa Arduino Leonardo.
Con esta configuración, compilamos el script y lo subimos a la placa.
Resultado
Abrimos el Monitor Serie, que debería aparecer en blanco ya que el texto de depuración está configurado al mínimo por defecto.
Ahora tocamos el extremo libre del cable puente y veremos cómo se ilumina el led, y cómo se detecta el "toque" en el Monitor Serie.
También verás que el script acumula los "toques" para poder utilizarlos en otros programas.
Al código
Brevemente explico lo que hace el programa:
Durante el setup(), iniciamos la comunicación por serie, activamos el pin para el led, y rellenamos la matriz de lecturas analógicas con unas lecturas iniciales con setTouch (Por defecto se leen 20 valores).
void setup() {
// initialize serial communication at 115200 bits per second:
Serial.begin(115200);
//enable LED pin
pinMode(LP,OUTPUT);
setTouch(TP);
}
A continuación, pasamos a la función de ejecución continua loop() donde refrescaremos las lecturas al inicio de cada bucle.
setTouch(TP);
//------loop continues------//
void setTouch(int tp){
//fill up the array
for(int a=0;a<VALUES;a++){
mean[a]=analogRead(tp);
delay(10);
}
}
Comprobamos si la depuración es mínima o extendida y ejecutamos el programa principal correspondiente (es el mismo, pero con mayor salida al Monitor Serie).
Calcula la diferencia de tensión entre la última lectura y la media,
Si el valor es mayor que la caida de tensión de referencia, se identifica "toque", se imprime por Monitor Serie, se suma al contador y se ilumina el led.
Se restaura la media a 0, se apaga el led y se devuelve la variable de "toques".
int minimal(int tp){
//start reading values
for(int i=0;i<VALUES;i++){
//get average of array
for(int j=0;j<VALUES;j++){
avg+=mean[j];
}
avg=avg/VALUES;
//read current value
fin=analogRead(tp);
//compare average with current value
if((avg-fin)>REFV){
Serial.println("---------- T O U C H ----------");
//increase counter
touch++;
//print touch count
Serial.print("T: ");
Serial.println(touch);
//light led
ledON(LP);
//wait 1 second after touch detected
delay(1000);
}
//restart counter
avg=0;
//restart led
ledOFF(LP);
delay(VALINTERVAL);
}
return touch;
}
Con esto se reinicia el bucle, volviendo al punto 2.
Otras configuraciones
El script cuenta con diversas variables de configuración que encontraremos en los '#define's
OUT: define la cantidad de texto de depuración. Valor 0: mínimo; Valor 1: extendido.
VALUES: tamaño de la matriz de valores analógicos.
TOUCHPIN: definir el número del pin analógico a usar.
LEDPIN: definir el número de pin usado por el led.
VALINTERVAL: intervalo de evaluación en milisegundos.
REFV: valor de caida de voltaje (en milivoltios) que identifica un "toque".
// OUTPUT verbosity
// 1: extended --> Prints ADC array, mean value, touch count and touch message with touch count
// 0: minimal --> Prints touch message with touch count
#define OUT 0
// comparison variables
#define VALUES 20 //values for mean array
#define TOUCHPIN 3 //touch pin
#define LEDPIN 20 //led pin
#define VALINTERVAL 100 //evaluation interval
#define REFV 80 //reference voltage drop in mV
Calibración
Cambiando la configuración se puede calibrar el script para distintas placas. Con lo que he probado, esta es una buena secuencia:
Define una REFV alta, por ejemplo 1000
Carga el script con depuración extendida (OUT 1)
Abre el Monitor Serie y observa los valores medios (Average: 230)
Ahora, toca el cable puente y observa de nuevo los valores medios (Average: 170)
Haz la resta entre las medias en 3. y en 4. (230-170=60).
Define el REFV con este valor y calibra a partir de aquí.
Para más sensibilidad, defínelo más bajo (50).
Si es excesivo puede detectarse sin contacto. Auméntalo (80).
Debido a la carga del CPU, REFV puede variar si hay depuración OUT 1 respecto a OUT 0. Busca un valor que funcione para OUT 0.
¿Te ha sido útil o tienes dudas? Deja tu comentario en 🐦 Twitter!
Si has usado software GIS de escritorio, te sorprenderá saber que muchas de esas herramientas geoespaciales también están disponibles para tus mapas web mediante una librería javascript gratuita y de código abierto.
Hoy te enseño TurfJS. Pulsa el botón 🌤 del mapa incrustado y verás una de sus muchas posibilidades: la interpolación por inverso de la distancia al cuadrado o IDW.
Contenido
Instalación
TurfJS cuenta con una práctica web que nos explica todas las funciones disponibles, así como la configuración inicial:
Puedes crear un mapa base, por ejemplo, con Leaflet. Además, TurfJS utiliza sintaxis JSON, por lo que es altamente compatible con las funciones geoJSON de Leaflet.
TIP! Leaflet ha sido recientemente actualizado a la versión 1.9.1. Este post todavía no está actualizado y utiliza la versión 1.7.1.
markers.forEach(function(m){
var marker=L.marker([m.lat, m.lon]).addTo(map);
marker.bindTooltip("<b>"+m.name+"</b><br>"+m.dato).openTooltip();
});
//ASÍ AÑADÍAMOS UN MARCADOR ÚNICO A LEAFLET
//var marker = L.marker([25.77, -80.13]).addTo(map);
//marker.bindTooltip("<b>Estacion1</b><br>m.dato").openTooltip();
Esto lo hace empleando uno de los métodos más habituales: el inverso de la distancia al cuadrado (IDW) o inverse distance weighting para una potencia de 2.
//vector de puntos vacío
var points=[];
//bucle para rellenar el vector de puntos con los datos
markers.forEach(function(m){
points.push(turf.point([m.lon, m.lat],{name: m.name, dato: m.dato}));
});
//convertir a featureCollection de Turf
points=turf.featureCollection(points);
TIP! Fíjate que Turf recibe las coordenadas en longitud-latitud, en lugar del habitual latitud-longitud!
Definir las opciones de la interpolación, entre las que podemos elegir las siguientes:
gridType: o tipo de malla
points (puntos)
square (cuadrados)
hex (hexágonos)
triangle (triángulos)
property: propiedad (o campo) del objeto de puntos que contiene los valores a interpolar.
units: unidades espaciales usadas en la interpolación
miles: millas
kilometers: kilómetros
radians: radianes
degrees: grados
weight: peso o potencia a la que se eleva la distancia, habitualmente 2, aunque valores más altos darán un resultado más suavizado.
//opciones de interpolación: malla rectangular de la variable 'dato' en kilómetros usando potencia de 2
var options = {gridType: 'square', property: 'dato', units: 'kilometers', weight: 2};
Por último, generar la malla interpolada y añadirla al mapa, siendo el valor numérico el tamaño de las celdas de la malla interpolada (0.5 Km):
//crear malla de Turf
var malla=turf.interpolate(points, 0.5, options);
//añadir a Leaflet
L.geoJSON(malla).addTo(map);
Siendo este el resultado (nada concluyente):
Dar formato a la malla y mostrar valores
Aunque no lo parezca, la interpolación está hecha, aunque no se está mostrando correctamente.
Voy a añadir una escala de color que represente los datos, y también a mostrar los valores al pasar por encima de las celdas. Para ello, modificamos las propiedades del objeto geoJSON de Leaflet:
style: función para modificar el estilo de los objetos, evaluando su valor.
onEachFeature: función que se añade a cada objeto para interactuar con él.
TIP! Descarga siempre los ficheros de la fuente oficial que enlace yo, su autor. No ejecutes un fichero compartido por teceros. El código javascript puede ser fácilmente manipulable con fines maliciosos.
Descripción
Eres un superviviente con munición ilimitada rodeado de zombis, cuánto aguantarás?
La aleatoriedad hace cada partida única: enemigos zombi aparecen aleatoriamente por los límites del mapa, con atributos aleatorios (velocidad y nivel), haciéndolos impredecibles.
Hay 4 niveles de zombi, haciéndolos más grandes y difíciles de matar (1×1, 2×2, 3×3, 4×4).
El juego es infinito siempre y cuando puedas seguir matando zombis.
Eso sí! Malgastar balas resta puntuación, así que mantén tus disparos certeros!
Instrucciones
Clicka o toca un punto del mapa para disparar a ese lugar.
Cada bala usada resta 1 punto (-1).
Disparar a partes del cuerpo (verde claro) suma 1 punto (+1).
Disparar a la cabeza (verde oscuro) suma 10 puntos (+10).
El tiempo de supervivencia sumará puntos en futuras versiones.
Características
Multi-platforma.
Portable (fichero .html ejecutable)
Ultraligero: 20 KB.
Sin-conexión: descarga y juega localmente.
Supervivencia infinita.
Aparición de enemigos aleatoria.
Tiempo de aparición aleatorio.
Atributos de enemigos aleatorios.
Velocidad
Nivel
Enemigos modulares (multi-parte)
Cuerpo (verde claro)
Cabeza (verde oscuro)
Sistema de puntuación
(Hit) Disparo al cuerpo +1
(Kill) Disparo a la cabeza +10
(Bullet) Bala usada -1
(Time) Tiempo de supervivencia [No implementado]
Animaciones
Futuros lanzamientos
Lluvia de ideas de características para futuras versiones:
menu de opciones
página resumen de puntuación final
gráficos más complejos
ciclo día-noche
sistema climático: nubes, lluvia, relámpagos
objetos de mapa: árboles, muros
enemigos complejos
saqueo de enemigos (balas, puntos)
poderes: ralentizar, matar todo el mapa, muerte a un disparo, etc
Desarrollo
Visita la página de desarrollo del juego en github
En este post explicaba directamente cómo añadir las estaciones de AEMET con ciertos datos, usando mi plugin leafMET para Leaflet (disponible en github):
Vamos a detenernos ahora en el detalle de cómo funciona la API y cómo acceder a todos los datos disponibles.
Contenido
Acceso a la API
Para usar la API necesitamos una clave de acceso, o API key. Accedemos a la página de AEMET OpenData y hacemos click en el botón "Solicitar" debajo de Obtención de API key.
A continuación se nos pedirá una dirección de correo electrónico para enviarnos dicha clave.
Nos enviarán un primer correo de confirmación, y a continuación recibiremos un segundo correo con la clave de acceso. La copiamos y seguimos.
Documentación y ejemplos
Volvemos a la página de AEMET OpenData y entramos al Acceso Desarrolladores.
Nos interesa la documentación dinámica, que muestra todas las peticiones de datos disponibles y que podemos probar directamente desde allí.
Podemos pulsar sobre cada tema y se mostrará la sintaxis a usar para cada petición de datos.
Vamos a desplegar observación-convencional y, dentro, /api/observacion/convencional/todas.
Veremos un icono de aviso rojo a la derecha, donde podemos introducir la API key y probar la petición.
Ahora pulsamos el botón Try it out! y nos dará:
un ejemplo de petición curl
la URL de la petición
el cuerpo, código y encabezados de la respuesta
Si abrimos la dirección URL que nos ofrece el campo "datos" veremos el listado completo de datos de todas las estaciones. A mi me sale todo este chorizo de JSON:
Cargando...
Los campos más interesantes son los siguientes:
idema: ID o código de la estación.
lat y lon: coordenadas de latitud y longitud.
alt: altitud de la estación.
fint: fecha del intervalo (horario) de los datos.
prec: precipitación registrada en ese periodo.
vv: velocidad del viento en la estación.
ta: temperatura ambiente en la estación
Petición de datos para web mediante javascript
Estructura HTML
Voy a desarrollar un ejemplo incrustado para acceder a los datos y hacer uso de ellos. Simplemente creo un documento .html con un botón de solicitud y una línea de texto y una función de solicitud:
Pulsa el botón para solicitar los datos
<html>
<body>
<button id="solicitud" onclick="solicitar()">Solicitar</button>
<p id="texto">Pulsa el botón para solicitar los datos</p>
</body>
<script>
function solicitar(){
document.getElementById("texto").innerHTML="Todavía no he programado eso..";
}
</script>
</html>
TIP! Copia y pega este código en un documento de texto con extensión .html para abrirlo en tu navegador.
Solicitud HTTP
Realizamos la solicitud de datos con javascript usando el objeto XMLHttpRequest. En w3schools nos ponen un ejemplo muy simple:
Como vimos usando la documentación dinámica, después de hacer la solicitud, recibimos un enlace para acceder a los datos, lo cual es otra solicitud, así que debemos anidar dos solicitudes así:
<script>
function solicitar(){
// Definir texto de carga
document.getElementById("texto").innerHTML="Cargando datos...";
// Definir la API Key
var AK=tUApiKey;
// Definir URLs de solicitud
var URL1="https://opendata.aemet.es/opendata/api/observacion/convencional/todas";
var URL2="";
// Crear objetos XMLHttpRequest
const xhttp1 = new XMLHttpRequest();
const xhttp2 = new XMLHttpRequest();
// Definir función de respuesta a la primera solicitud:
// Queremos acceder a la URL del campo "datos"
xhttp1.onload = function() {
// 1º Pasamos la respuesta a JSON:
URL2=JSON.parse(this.responseText);
// 2º Obtenemos el campo "datos":
URL2=URL2.datos;
// 3º Hacemos la nueva petición:
xhttp2.open("GET", URL2);
xhttp2.send();
}
// Definir función de respuesta a la segunda solicitud:
// Modificaremos la línea de texto de la página con información descargada
xhttp2.onload = function() {
// 1º Pasamos la respuesta a JSON (se trabaja mejor así):
var datos=JSON.parse(this.responseText);
// 2º Obtenemos la longitud del objeto JSON
// Esto equivale a registros horarios individuales por estaciones
var registros=datos.length;
// 3º Obtenemos la fecha del primer registro.
// Le daremos un formato más legible ya que está en ISO
var fechaini=datos[0].fint;
fechaini=new Date(fechaini+"Z");
fechaini=fechaini.toLocaleDateString()+" "+fechaini.toLocaleTimeString();
// 4º Obtenemos la fecha del último registro
// Le damos el mismo formato
var fechafin=datos[(datos.length-1)].fint;
fechafin=new Date(fechaini+"Z");
fechafin=fechafin.toLocaleDateString()+" "+fechafin.toLocaleTimeString();
// 5º Unimos la información en la línea de texto
document.getElementById("texto").innerHTML="Se han descargado <b>"+registros+"</b> registros desde el <b>"+fechaini+"</b> hasta el <b>"+fechafin+"</b>. <a href='"+URL2+"' target='_blank'>Ver todos los datos.</a>";
}
// Enviar la solicitud
xhttp1.open("GET", URL1+"/?api_key="+AK);
xhttp1.send();
}
</script>
Veamos el resultado a continuación:
Pulsa el botón para solicitar los datos
Y ya está listo! De esta forma puedes obtener las coordenadas y los valores de las distintas variables meteorológicas horarias de AEMET para usarlas en una tabla o en un mapa web como hemos visto para Leaflet.
Como siempre, deja tus dudas o comentarios en Twitter! 🐦
Lo habitual es ver y trabajar con estos ficheros en algún programa de SIG como QGIS o ArcGIS, pero ¿cómo lo acercamos al resto de usuarios ?¿No sería mejor verlo directamente en 3D y visitarlo virtualmente?
Aquí es donde me topé con CesiumJS: un visor de mapas en 3D al estilo del famoso [G] Earth, pero de código abierto, gratuito y altamente personalizable para hacer tus propios proyectos.
Demo del visor CesiumJS con la nueva cartografía del volcán Tajogaite con imagen satelital de IDE Canarias. Clicka en el mapa para interactuar con él, o ábrelo en su versión completa.
Contenido
Crear cuenta de Cesium ion
Para utilizar CesiumJS es necesario registrar una cuenta para crear un código de acceso o Access Token.
Además, registrándonos podremos subir nuestros propios ficheros y personalizar las vistas 3D que crearemos. Verás que en mi caso he subido el Modelo Digital de Superficies y la imagen satélite que usa el visor. Dejo aquí los enlaces por si quieres usar los mismos:
TIP! Puedes usar el servicio WMS en tu visor GIS para obtener una ortofoto completa, o descargar la que uso yo para este ejemplo (270 MB con resolución de 1m)
Incorporar CesiumJS a tu web.
Para usar CesiumJS configuramos un fichero .html como ya viene siendo habitual.
En este caso voy a copiar directamente uno de sus ejemplos ya que apenas tiene unas 25 líneas:
Como verás, las partes fundamentales son las siguientes:
enlaces a las librerías de CesiumJS en el <head>
un elemento <div> con id "cesiumContainer" dentro del <body>
el código de acceso dentro de la variable Cesium.Ion.defaultAccessToken. Introduce aquí la tuya.
una variable de visor llamada "viewer"
la configuración inicial del visor, especificando las coordenadas iniciales, y los ángulos de inclinación X y Z.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- Include the CesiumJS JavaScript and CSS files -->
<script src="https://cesium.com/downloads/cesiumjs/releases/1.97/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.97/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
<div id="cesiumContainer"></div>
<script>
// Your access token can be found at: https://cesium.com/ion/tokens.
// This is the default access token from your ion account
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1MTYwNGM0Yi1iYzlkLTRkMTUtOGQyOS04YjQxNmUwZDQ0YjkiLCJpZCI6MTA0Mjc3LCJpYXQiOjE2NjAxMDk4MzR9.syNWeKPLWA2eMrEh4K9hvkyp1oGdbLMaL0Ozk1kaksk';
// Initialize the Cesium Viewer in the HTML element with the `cesiumContainer` ID.
const viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: Cesium.createWorldTerrain()
});
// Add Cesium OSM Buildings, a global 3D buildings layer.
const buildingTileset = viewer.scene.primitives.add(Cesium.createOsmBuildings());
// Fly the camera to San Francisco at the given longitude, latitude, and height.
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(-122.4175, 37.655, 400),
orientation : {
heading : Cesium.Math.toRadians(0.0),
pitch : Cesium.Math.toRadians(-15.0),
}
});
</script>
</div>
</body>
</html>
El resultado ya lo habrás visto en varias ocasiones:
Y su código completo es este:
<html lang="en">
<head>
<meta charset="utf-8">
<script src="https://cesium.com/downloads/cesiumjs/releases/1.96/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.96/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body style="margin:0;width:100vw;height:100vh;">
<div style="height:100%;" id="cesiumContainer"></div>
<script>
// Your access token can be found at: https://cesium.com/ion/tokens.
// Replace `your_access_token` with your Cesium ion access token.
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1MTYwNGM0Yi1iYzlkLTRkMTUtOGQyOS04YjQxNmUwZDQ0YjkiLCJpZCI6MTA0Mjc3LCJpYXQiOjE2NjAxMDk4MzR9.syNWeKPLWA2eMrEh4K9hvkyp1oGdbLMaL0Ozk1kaksk';
// Initialize the Cesium Viewer in the HTML element with the `cesiumContainer` ID.
const viewer = new Cesium.Viewer('cesiumContainer', {
//terrainProvider: Cesium.createWorldTerrain()//terreno original
terrainProvider: new Cesium.CesiumTerrainProvider({//terreno modificado
url: Cesium.IonResource.fromAssetId(1255858),
}),
});
// Esconder widgest inferiores
viewer.timeline.container.style.visibility = "hidden";
viewer.animation.container.style.visibility = "hidden";
// Add Sentinel2 imagery
const layer = viewer.imageryLayers.addImageryProvider(
new Cesium.IonImageryProvider({ assetId: 1256129 })
);
// Add Cesium OSM Buildings, a global 3D buildings layer.
const buildingTileset = viewer.scene.primitives.add(Cesium.createOsmBuildings());
// Fly the camera to San Francisco at the given longitude, latitude, and height.
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(-17.8195, 28.6052, 3000),
orientation : {
heading : Cesium.Math.toRadians(-85.0),
pitch : Cesium.Math.toRadians(-30.0),
}
});
</script>
</div>
</html>
Y esto es todo! Verás que puedes crear infinidad de aplicaciones web inmersivas con terrenos detallados por los usuarios y más precisos que los genéricos.
¿Se te ocurre qué más hacer con CesiumJS? Cuéntalo en Twitter 🐦
Uno de mis últimos proyectos es una app de notas para alojarlas en mi servidor y prescindir de [G] Notes.
La característica más interesante que incluye es Rich Text Editor, una completa librería javascript en forma de interfaz para dar formato HTML al texto de cualquier formulario web.
Es de código abierto, gratis para uso personal, y muy simple de usar. Todo un win-win que te traigo hoy.
Contenido
Licencia
He dicho que Rich Text Editor es gratis, y es así si le das un uso no comercial.
Es importante que leas su página de precios para que entiendas las limitaciones de la licencia gratuita:
1 desarrollador
1 dominio
5 usuarios activos
En mi caso, he instalado Rich Text Editor en este servidor que aloja el blog, y lo he restringidomediante php para que solo se pueda acceder desde el propio dominio.
TIP! No hospedes los archivos en cualquier sitio que sea accesible por cualquiera (como un servidor público), ya que podrías dar pie a un incumplimiento de licencia, teniendo que pagar una licencia standard, o dando de baja tu app.
Aun así, las licencias comerciales son de un único pago y puede que te merezca la pena obtener una si vas a darle más uso.
Instalación
Usar Rich Text Editor es muy sencillo, y su página de documentación lo explica a la perfección.
Para instalar Rich Text Editor solo tenemos que descargar los ficheros de su página web a una carpeta local o a una ubicación de tu servidor.
Una funcionalidad básica es capturar el texto que hemos ido editando en el cuadro de texto. Es lo que hace el botón de la parte inferior, usando la función getHTMLcode():
<button onclick="alert(editor1.getHTMLCode())">Show Html Code</button>
Usos
Como comenté anteriormente, el uso que le voy a dar a esta librería es en un editor de notas que se alojará en mi servidor.
El editor de texto me sirve para dar formato a las notas muy fácilmente y guardarlas con todo el formato.
Aquí tienes una demo de cómo va quedando la interfaz, aunque no tiene las funciones habilitadas todavía.
Otras aplicaciones en las que puedes usar este editor es en foros, formularios, o cualquier cuadro de texto que tenga que rellenar un usuario de tu web app.
Espero que te resulte tan útil como a mí y que le des buen uso. Si tienes dudas o comentarios puedes enviarlos por Twitter 🐦 Hasta pronto!