- Vinete de Cucharete, por Cucharete
- Eventos en Flex, por Alberto Alcaraz en Made in Flex
- Modelando con ArgoUML aplicaciones en PHP, por Deuteros
- Escalabilidad en el servidor, por Joaquín Cuenca en Programa con Google
- Adobe Photoshop Express: Photoshop goes online with Flex (an AIR version coming soon), por Marco casario
- Testing the New WURFL API, por Jason Delport
- Montar un negocio sin experiencia, por Fraçois Derbaix
- First Tuesday Valencia: 1 de Abril de 2008, por Ruben Colomer
De relax en vacaciones: Fallas y Semana Santa
Aunque no lo parezca, los profesionales del sector TI también descansamos, apagamos los portátiles y BlackBerry’s (o casi) y utilizamos el móvil sólo para quedar con los amigos. Es lo que he estado haciendo los últimos días.
Valencia en Fallas
Comenzamos disfrutando de las Fallas el fin de semana. Aquí tenéis el álbum completo de fotos de Fallas. Valencia en Fallas huele a churros y buñuelos y sabe a pasacalles, petardos y caminatas a pié. Si no soportas los petardos ni se te ocurra. Para un gallego como yo lo más impresionante son, sin duda, las mascletàs, no puedes perderte al menos una en la Plaza del Ayuntamiento y otra en alguna falla de barrio, se viven y se sienten mucho más cercanas.
Fallas es un buen momento también para hacer turismo ya que de paso que caminas para ver fallas te vas cruzando con lo más importante de Valencia. No podéis dejar de pasear por el Barrio del Carmen estos días, os enamorará. Aquí os dejo las estampas más típicas de Valencia.
Después de patearnos Valencia el domingo y lunes, el martes por la tarde decidimos (mi pareja y yo) olvidarnos de la Nit del Foc y de la Cremà y nos vinimos a Pontevedra a pasar las vacaciones con mi familia y amigos, de miércoles a lunes, puesto que estos días también son fiesta en Valencia este año, total seis días, cinco si no contamos el lunes puesto que es el viaje de vuelta.
Cambio de aires
Este año ha habido suerte puesto que casi todos los colegas andaban por aquí y hemos podido comer y beber ampliamente. Antes, por supuesto, hemos pasado horas y horas con mis sobrinitas, Nerea de casi cinco añitos y Aroa de nueve meses recién cumplidos.
No habíamos previsto un cambio de tiempo tan brutal, pasamos de estar a 24º en Valencia y en manga corta desde primeras horas de la mañana a 8º cuando paramos a tomarnos el bocata (cuando viajamos juntos nos gusta parar en un área de descanso y comer tranquilamente al aire libre si el tiempo lo permite, sino nos vamos a alguna cafetería). Esta vez parecía que el tiempo lo permitía pero menudo susto.
Santiago de Compostela
Al llegar a casa de mis padres, más de lo mismo, un frío que pela. Pero a aguantar toca que son vacaciones. El miércoles, aprovechando que la mayoría del país no había comenzado aún las vacaciones y habría poco turista, nos dimos una vuelta por Santiago. Hicimos una ruta típica, entrando por la Porta do Camiño, subiendo hasta la Praza da Quintana, Praterías y Praza do Obradoiro con la catedral. Para la vuelta, bajamos por la Rúa do Franco, recordando tiempos pasados de juergas nocturnas, y regresamos por Rúa do Vilar y Praza do Toural. Santiago siempre impresiona, aunque se te quede la cara morada del frío :).
Combarro
El viernes nos acercamos a Combarro a dar una vuelta y a hacernos unas fotos, pero ya se hacía un poco desagradable debido a la avalancha de gente que había. Por cierto, no me han gustado nada las escaleras de madera que han puesto para evitar tener que subir por las piedras de toda la vida, además de no quedar bien inspiran poca confianza. Las fotos no son excesivamente buenas, había demasiada gente. Combarro es mucho más bonito e impresionante de lo que se ve en estas tres fotos.
Ya por la noche, churrascada como nos merecemos y a hacer un poco el tonto para bajar la comida. Dejo las fotos de las comidas a Marcos pues ya sabemos que es su especialidad :P.
Para finalizar la noche, algún café y alguna copa mientras manteníamos una animada charla entre buenos amigos.
Pontevedra
El sábado dimos una vuelta por la zona vieja de Pontevedra, paseando y haciéndonos unas fotos. A mi pareja le encanta callejear, qué le voy a hacer, en Valencia apenas lo hacemos y aquí no paramos.
Para quién no lo sepa, Pontevedra es la gran desconocida de Galicia. Tiene el casco antiguo más importante de Galicia después de Santiago.
El casco histórico de Pontevedra está dominado por la piedra, los escudos heráldicos y los soportales, casi todas las plazas los tienen y le dan un encanto especial (además de resguardarte los días de lluvia). Puedes tomarte unas cañas en alguno de los bares de la Praza da Verdura o unas tapas en la Praza da Leña, con su hermoso cruceiro del s.XV. Desde la Praza do Teucro, en homenaje al arquero griego Teucro, fundador de la ciudad según cuenta la leyenda, llegas a la Praza das 5 rúas, centro neurálgico de la marcha nocturna de la ciudad y donde se encuentra la casa donde vivió Valle Inclán.
Las vacaciones ya han terminado, mañana toca viaje de vuelta, y lo peor es que este año, al caer Semana Santa tan pronto, tendrán que pasar más de cuatro meses hasta volver a tener vacaciones.
Creando sencillas mashups y widgets con Google Ajax Feed Api
No cabe duda que uno de los mayores avances que se han producido en Internet ha sido el modelo de compartir contenidos generados. Desde la burbuja de 2001, donde se protegían los contenidos hasta el extremo, hemos llegado a un modelo donde prima lo contrario, compartirlos todo lo posible, cuantos más te lean mejor, da igual el medio. En esto han jugado un papel fundamental los sistemas de sindicación, concretamente los RSS. Hoy en día casi todo tiene feeds, da igual el tipo del contenido, no sólo artículos de texto, también fotos, vídeos, twitters y demás.
La teoría
La base de la mayoría de mashups o widgets es la combinación de distintos feeds y su posterior tratamiento y utilización. Un clásico cómo la combinación de fotos de Flickr con mapas de Google es sencillo gracias a los RSS con la información de la geolocalización del primero. La dificultad a la hora de gestionar los feed es la falta de una librería Javascript que permita leer y parsear feeds de cualquier tipo en el navegador sin ayuda de herramientas externas, por lo que hasta ahora lo normal era hacerlo del lado del servidor y devolver la lista de items del feed mediante JSON o simples arrays. Por suerte Google ha pensado en nosotros y nos ha dado la llave de la cerradura. La ventaja de hacerlo así es que todo el trabajo cae en el navegador cliente liberando a nuestro servidor de cargar los XML, parsearlos y hacer el tratamiento posterior. Además, para evitar problemas, tendríamos que cachearlos si no queremos que el rendimiento de nuestras máquinas caiga. Google ya lo hace todo por nosotros. Sí, también ha pensado en eso y no los recarga siempre que los llamas.
Con Google Ajax Feed Api es todo más sencillo puesto que es capaz de leer la mayoría de formatos de feeds y los devuelve en un objeto Javascript fácil de tratar. Para explicar su funcionamiento, nada mejor que un ejemplo práctico.
Hoy en Bricomanía…
Este es nuestro proyecto de hoy. Gracias a Marcos por su cuenta de vídeos en Youtube puesto que yo no tengo. Un pequeño widget con el que visualizar nuestra actividad en los servicios más populares de Internet. Lo puedes poner en el lateral de tu blog 😛 o añadir a tu MySpace.
La prática
Lo primero que haremos será registrar nuestro dominio para utilizar la API, con lo que obtendremos el API Key. Recuerda que esta clave va asociada al dominio y no podrás utilizarla en otro.
Lo siguiente será buscar las URLs de los feed’s a utilizar. En nuestro caso buscamos las de Twitter, Flickr, Youtube, Del.icio.us y LastFM:
- http://api.flickr.com/services/feeds/
photos_public.gne?id=24422272@N02&format=rss2〈=es_us - http://www.youtube.com/rss/user/mgvideos/videos.rss
- http://twitter.com/statuses/user_timeline/osus.rss
- http://del.icio.us/rss/jiglesias
- http://ws.audioscrobbler.com/1.0/user/jiglesias/recenttracks.rss
Vosotros deberéis utilizar las vuestras, claro, tampoco es que yo tenga mucho interesante que ver o leer.
Veamos como hacerlo con uno de los feeds, los demás se harían del mismo modo. El resto del widget lo montáis como más os guste según lo que queráis hacer. En el caso de nuestro ejemplo definimos una capa (DIV) para cada feed y con los botones de las imágenes cambiamos el estilo display ocultando todas y mostrando la que se ha pulsado. Al final del articulo tenéis el código completo del ejemplo.
Lo primero que debemos hacer es cargar el Javascript de la API con el Api Key que obtuvimos. Ahora, como veis el código inferior, instanciamos la api y definimos una función de callback que se encargará de cargar nuestros feeds cuando se haya cargado la página. En esta función podemos añadir todo el código que queramos ejecutar para iniciar la aplicación. En nuestro caso vamos a cargar nuestro Twitter.
<script src="http://www.google.com/jsapi?key=ABQIAAAAVTgpBmlTiDeEs9UaP69iNRT4lrLtf4G1Npt4CNmX07qB4fHehhQysXL6wgFRWD8d-X_v8iu6Hs2orA" type="text/javascript"></script> <script type="text/javascript"> function OnLoad() { loadTwitter(); } google.load("feeds", "1"); google.setOnLoadCallback(OnLoad); </script>
Ahora llega realmente el proceso. Veréis que pocas líneas son necesarias para parsear y mostrar tu Twitter.
function loadTwitter() { var feed = new google.feeds.Feed("http://twitter.com/statuses/user_timeline/"+usertwitter+".rss"); feed.setNumEntries(50); feed.load(function(result) { var container = document.getElementById("twitter"); if (!result.error) { var entries=result.feed.entries; var html=""; for(var i=0; i<entries.length; i++){ var entry = entries[i]; var titulo=String((entry.title)).substring(String(entry.title).indexOf(":")+1); html+="<p><b>"+fecha(entries[i])+"</b><br/>"+string_create_urls(titulo)+"</p>"; } container.innerHTML=html; }else{ container.innerHTML="<p>Error cargando el feed</p>"; } } ) }
El código casi se explica por sí mismo. Se carga el feed y se define la función que se ejecutará cuando esté cargado. Podemos incluso definir la cantidad máxima de items que se cargarán. Y ya está. Tenemos una colección de objetos con todos los datos, sólo queda iterar sobre ellos y mostrarlos como nos parezca oportuno. Nosotros nos hemos apoyado en un par de funciones auxiliares para mostrar la fecha y los links de nuestro Twitter.
Ahora me diréis: ¡traición! has utilizado innerHTML después de no recomendarlo en un artículo anterior. Bueno, de acuerdo, no tenía ganas de añadir más código y tampoco voy a necesitar acceder a lo añadido :-P, no es una buena excusa pero ahí queda eso. Sigo recomendando que utilicéis DOM para añadir los nuevos elementos. Creo que sería un buen tema para un artículo posterior puesto que el procedimiento es bastante engorroso, por eso innerHTML es tan popular.
Y eso es todo amigos. En el próximo programa veremos cómo posicionar las fotos de Flickr en Google Maps.
Generando Zips bajo demanda en PHP
Puede parecer ridículo el objeto de este artículo, pero a más de uno le interesará.
Uno de nuestros partners va a implementar en su portal móvil un buscador gestionado por JumpTap y solicitaba hace unos meses a los proveedores que trabajábamos para él que les enviásemos un archivo XML con nuestro catálogo de contenidos. El archivo XML debería estar zipeado y además disponible en una URL para poder actualizarlo periódicamente. Como resulta obvio, en un escenario normal, tendríamos que generar manualmente este archivo, algo a lo que, por supuesto, no estábamos dispuestos. Teníamos además un problema adicional. Nuestra plataforma de portales móviles es físicamente única, sólo hay una instancia en disco, con lo cual los archivos generados tendrían que tener distintos nombres para que el proveedor pudiese descargar cada uno sin sobreescribirlos cada vez que quisiera actualizarlos. Además el nombre del archivo debería ser el utilizado para la descarga, es decir, si el archivo iba a ser comprimido.zip, habria que descargar ese archivo, no llamar a un compromido.php que devolviese el zip con una cabecera:
header("Content-Disposition: attachment; filename="comprimido.zip"");
o hacer un redirect final al zip. Esto es debido a que al automatizar el proceso necesitaban descargar automáticamente el archivo. Piensa que un file_get_contents o abrir un socket para recoger el archivo no interpretará esos datos.
Primero el servidor
Hay que decirle a Apache que cuando llamen a determinados archivos ZIP de nuestro servidor se llamará a un script PHP. Podríamos hacer que la extensión .zip fuese parseada automáticamente por PHP, pero entonces ya no podríamos descargar nunca un ZIP normal. La mejor opción: mod_rewrite:
RewriteEngine on RewriteCond %{REQUEST_URI} ^(.+)catalogo_(.+).zip$ RewriteRule ^(.+)catalogo_(.+).zip /catalogo_zip.php$1
Es decir, cada vez que se llame a loqueseacatalogo_loquesea.zip, el servidor web llamará intermente al script catalogo_zip.php. En nuestro caso, los archivos estaran formados por el nombre del host desde el que se llaman, así cada host o portal tiene un archivo diferente, por ejemplo catalogo_portal1.dominio.com.zip, catalogo_portal2.dominio.com.zip, catalogo_dominio2.com.zip. El proveedor del servicio recogería los archivos desde:
- http://portal1.dominio.com/catalogo_portal1.dominio.com.zip
- http://portal2.dominio.com/catalogo_portal2.dominio.com.zip
- http://dominio2.com/catalogo_dominio2.com.zip
respectivamente, respetando las normas que hemos impuesto: distintos archivos para distintos portales.
Ahora el script
Ahora que Apache ya sabe como interpretar nuestros ZIP’s falsos, llega el momento de generar realmente los archivos. Para eso hacemos uso de las funciones ZIP de PHP que nunca había utilizado pero que me han sorprendido gratamente..
$xml="<nuestro XML generado />"; $filename="descargas/catalogo_".$_SERVER['HTTP_HOST'].".zip"; if(file_exists($filename)) unlink($filename); $zip = new ZipArchive(); $zip->open($filename, ZIPARCHIVE::CREATE); //guardamos nuestro archivo XML $zip->addFromString("catalogo_".$_SERVER['HTTP_HOST'].".xml", $xml); $zip->close(); //mostramos el zip header("Content-Type: application/zip"); header("Content-Length: ".filesize($filename)); readfile($filename); unlink($filename);
Básicamente creamos un nuevo archivo, añadimos el XML y lo lanzamos como respuesta a la solicitud HTTP. La magia es la función addFromString que nos evita tener que escribir primero el XML a un archivo auxiliar. Se podrían añadir otros archivos externos con addFile si fuese necesario.
Y eso es todo, con pocas líneas de código hemos hecho la vida un poco más sencilla.
Redimensionando GIF’s desde PHP y II
En el primer artículo de esta serie de dos comentamos cómo escalar GIF’s con transparencias.
Recordando conceptos, lo más importante es que la transparencia es un atributo de la imagen, no del color en sí mismo, de manera que se configura un color determinado como transparente y cualquier pixel que tenga el mismo color exacto será transparente.
Veamos ahora qué ocurre si el gif es animado. Si intentamos abrir la imagen con imagecreatefromgif, nos llevaremos la sorpresa de que sólo aparece el primer frame. Ese es el comportamiento normal, ignorar el resto.
La teoría
Un GIF animado está formado por una secuencia de imágenes GIF añadiendo cierta información adicional, como el tiempo que se muestra cada frame. La trasnparencia es común a todos los frames, es decir, vuelve a ser un atributo de la imagen final. Pero esto no es todo, según investigábamos el tema nos encontramos con dos detalles adicionales que entorpecían todavía más el proceso. El primero es que no todos los frames tenían que ser del mismo tamaño, sino que cada uno podía tener un tamaño distinto y, dentro de la información adicional que comentábamos antes, tendríamos los datos de la posición (x,y) donde situar ese frame respecto al primero. Vale, ahora que lo sabemos podemos tomar medidas en el proceso final. Pero cuando estas medidas están contempladas aparece otro problema. El concepto tradicional de secuencia de gif no es tal, en realidad hay dos modos de manejar las capas, según averiguamos con el Gimp:
- replace: es el comportamiento normal, cada frame reemplaza al anterior, es como una película.
- combine: los frames se sobreponen unos sobre otro, de manera que, con las transparencias de cada frame, se genera una animación cambiando sólamente la parte de la imagen que cambia. El resultado es un archivo de menor peso.
Después de mil vueltas no encontramos la manera de combinar los frames, sólo podíamos utilizar replace. Llegados a este punto el problema eran los gifs animados con frames de distintos tamaños y combinados.
Antes de entrar en materia con el código, explicaremos a grandes rasgos cómo hacerlo, una vez entendamos el procedimiento será más sencillo ver el código. La solución pasa por separar el GIF animado original en sus frames, tratar cada frame por separado y volver a unirlos generando el nuevo gif animado. A la hora de tratar cada gif tenemos el problema descrito anteriormente con tamaños y replace/combine. El resultado fué generar un animado de tipo replace apartir del combine original fusionando cada frame con el inmediato anterior. Expliquemos esto más detalladamente. Según la teoría vista hasta ahora, en un GIF de tipo combine lo que se ve en cada momento es la superposición del frame actual más todos los anteriores. Es de suponer, además, que tendrán transparencias, puesto que sino uno sustituiría completamente a los otros. Por ejemplo, supongamos que tenemos un GIF de 5 frames y la reproducción va por el tercero. En ese momento estaríamos viendo el primer frame con el segundo encima, que al aplicar la transparencia dejaría ver el primero donde tengamos transparencia y encima de todo nuestro tercer frame que dejaría ver los de abajo. Una imagen vale más que mil palabras:
En esta imagen se muestra todo lo explicado. En la fila superior vemos la animación desglosada por frames. Les he puesto fondo gris para que lo veáis, pero en realidad sería transparente. Las líneas discontínuas representan el tamaño de esa capa sobre el tamaño total de la imagen representada por el primer frame. Como podéis apreciar hay variedad en cuanto a tamaño y posiciones. En la fila de abajo he ido fusionando los frames a medida que se van viendo. El segundo frame sería la fusión del primero con el segundo de la fila de arriba, que es en su mayoría transparente y solo añade unas pinceladas sobre el diseño inicial. El tercero es la fusión del segundo con el tercero de arriba, etc. Es el efecto combine, combina todas las capas que va mostrando. El resultado de la fila de abajo es una secuencia replace, cada frame es independiente del otro. Este es nuestro objetivo, convertir una imagen inicial tipo combine en una replace.
Esta sería la imagen original para que entendáis el efecto final..
La práctica
Ahora que ya sabemos qué es lo que tenemos que hacer surge la pregunta del cómo.
Para empezar os preguntaréis cómo separamos un gif animado en sus frames y los volvemos a juntar si las funciones de PHP sólo se quedan con el primer frame. Sencillo, con otras funciones. Aquí es donde vienen a nuestra ayuda un par de estupendas clases realizadas por László Zsidi, GIFEncoder y GIFDecoder. Su función es básicamente lo que dice su nombre, coger un gif y separarlo en sus frames o coger varios frames (o imágenes sueltas en disco) y devolver un GIF animado. El cómo no nos importa, queda fuera del alcance de este artículo y puedes consultar el código de las clases si te interesa. Simplemente comentaré que lo hace todo a nivel de bits, no utiliza ninguna función gráfica predefinida.
Al final de todo tenéis un zip con todo el código fuente y las clases necesarias.
include(dirname(__FILE__)."/GIFDecoder.class.php"); include(dirname(__FILE__)."/GIFEncoder.class.php"); $gifcontents=file_get_contents("miarchivo.gif"); if(is_gif_animado($gifcontents)){ $GIFS=new GIFDecoder($gifcontents); $ARRS=$GIFS->GIFGetFrames(); $OFFSETS=$GIFS->GIFGetOffset(); }
Este sería el principio. Es importante comprobar si es una animación, puesto que sino será todo mucho más sencillo como vimos en el primer capítulo. La función la encontraréis en el código. De aquí, lo importante que nos queda es
- $GIFS->GifGetFrames() contiene los frames del gif
- $GIFS->GifGetOffset() las desviaciones en la posición de cada capa.
Con esto podemos comenzar a operar sobre cada gif a través del array ARRS. El código está explicado en los comentarios. Es importante anotar que tanto el color de la transparencia como el tamaño de la imagen lo obtenemos con las funciones estandard. Esto lo hacemos así porque no encontré la manera de saber el color de transparencia a traves del GIFDecoder y, ya que tenía la imagen abierta, saco el tamaño.
//con las funciones estandard detectamos el tamaño del primer frame y el color de transparencia $srcImage = imagecreatefromgif($ipath); $transcolor=imagecolortransparent($srcImage); $w=imagesx($srcImage); $h=imagesy($srcImage);for($i=0; $i<count($ARRS); $i++){ //el primer frame se queda como esta if($i>0){ $image=imagecreatefromstring($ARRS[$i]); $wo=imagesx($image); $ho=imagesy($image); $off=$OFFSETS[$i]; //fusionamos el frame con el anterior $temp=imagecreatetruecolor($w, $h); $temp2=imagecreatefromstring($ARRS[$i-1]); //hay que tener en cuenta la posible transparencia, sino no hacemos nada if($transcolor!=-1){ $trnprt_color = @imagecolorsforindex($image, $transcolor); $trnprt_indx = @imagecolorallocate($temp, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue']); if($trnprt_indx){ imagefill($temp, 0, 0, $trnprt_indx); imagecolortransparent($temp, $trnprt_indx); imagecolortransparent($image, $transcolor); } } //creamos una imagen con el frame anterior y superponemos el actual con el offset necesario imagecopymerge($temp, $temp2, 0, 0, 0, 0, $w, $h, 100); imagecopymerge($temp, $image, $off[0], $off[1], 0, 0, $wo, $ho, 100); //capturamos el nuevo frame ob_start(); imagegif($temp); $GIFS->ARRS[$i]=ob_get_clean(); //ya no hay offsets puesto que todos tienen el mismo tamaño $GIFS->offsets[$i][0]=0; $GIFS->offsets[$i][1]=0; imagedestroy($temp); } }
En este punto tenemos exactamente el proceso que hemos estado explicando. Todos los frames tienen el mismo tamaño y son independientes, es decir, se han ido fusionando hacia arriba de manera que podemos hacer una animación replaced. Interesante es la manera de recuperar el gif de cada frame modificado para restaurarlo al array original. No tenemos otra forma de recuperar el contenido de una imagen que no sea mostrándola o guardándola a un archivo, así que la mostramos y la recuperamos del buffer de salida.
Ahora podemos hacer lo que necesitemos con la imagen, escalarla, imprimir texto, rotarla, etc. Nosotros la escalaremos, haremos un thumbnail de 90×90. Tan sencillo como hacer el proceso en todos y cada uno de los frames.
//ahora si queremos podemos redimensionar los frames, por ejemplo hacemos thumbnail for($i=0; $i<count($ARRS); $i++){ $image=imagecreatefromstring($ARRS[$i]); $wo=imagesx($image); $ho=imagesy($image); $dst_img=imagecreatetruecolor (90, 90); imagecopyresized($dst_img, $image, 0, 0, 0, 0, 90, 90, $wo, $ho); ob_start(); imagegif($dst_img); $ARRS[$i]=ob_get_clean(); }
Para mostrar la imagen, se necesite o no guardarla en disco, es necesario pasarla a un archivo, pues es el proceso que hace la clase GIFEncoder. Si después no la necesitamos se puede eliminar directamente como hacemos en el ejemplo.
$gif = new GIFEncoder($GIFS->ARRS, $GIFS->GIFS->GIFGetDelays(), 0, 2, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue'], array(), "bin" ); $fh = fopen("final.gif", 'w'); fwrite($fh, $gif->GetAnimation()); fclose($fh); header("Content-type: image/gif"); readfile("final.gif"); unlink("final.gif");
Os dejo el enlace con el código fuente y el ejemplo funcionando.
Como véis el proceso es bastante complejo, pero una vez se entiende el funcionamiento del GIF animado todo es más sencillo. Como comentario adicional, si váis a implementar un sistema de escalado en tiempo real, yo pensaría en cachear los resultados por aquello de optimizar un poco el sistema y no sobrecargar la cpu, después de todo estamos hablando de trabajo con imágenes, algo bastante pesado en memoria.
Finalmente, utilizaba inicialmente la función de comprobación de que una imagen es animada de László Zsidi, sin embargo detectamos que, en determinadas situaciones, entraba en un bucle infinito y terminaba saturando nuestro Apache de procesos ocupados en el bucle infinito. Menos mal que tenía otra versión más antigua de una función que hacía lo mismo. Deuteros me lo agradece enormemente.
Precargar CSS con la aplicación Flex
A la hora de crear una aplicación Flex con temas (skins) intercambiables o personalizables, uno de los problemas principales con el que nos encontramos es la precarga del mismo. En general tendríamos por un lado nuestro swf de la aplicación y por el otro el del css que estamos cargando. Asumiendo que cargásemos el tema en el evento creationComplete con
StyleManager.loadStyleDeclarations("micss.swf", true )
tendríamos un problema de sincronización. Los usuarios no verían el look&feel de nuestra aplicación hasta que se hubiese cargado el swf del tema, mientras tanto se vería el tema por defecto, lo que ofrece una imagen bastante pésima de la aplicación.
Para solucionarlo recurrimos a precargar el tema junto a la aplicación, de manera que cuando lancemos el evento creationComplete, el swf está ya en la caché del navegador y casi instantáneamente se aplica sin que el usuario note apenas retardo.
Para hacer la precarga personalizada de la aplicación deberemos extender el componente DownloadProgressBar. Además de para nuestro propósito de cargar el css, podemos también aprovechar para traducir los textos de la percarga, de manera que aparezcan en castellano, un detalle para los usuarios.
Os dejo el código de ejemplo.
Lo primero será decirle a nuestra aplicación que utilice nuestra precarga personalizada y cargar nuestro swf de estilos. Recuerda que cuando se ejecute el evento creationComplete, este swf estará ya en caché del navegador, no se cargará de nuevo desde el servidor.
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="StyleManager.loadStyleDeclarations("micss.swf", true )" preloader="com.xplota.mainloader.AppProgressBar">
Ahora veremos rápidamente como personalizamos el componente.
package com.xplota.mainloader{ import mx.preloaders.*; import flash.events.ProgressEvent; import flash.text.TextFormat; import mx.controls.Image; import flash.display.Sprite; import flash.events.Event; import mx.events.FlexEvent; import mx.events.RSLEvent; import flash.display.Loader; import flash.net.URLRequest; public class AppProgressBar extends DownloadProgressBar{ private var loader:Loader; private var _preloader:Sprite; public function AppProgressBar() { super(); //Configuramos las etiquetas downloadingLabel="Cargando..." initializingLabel="Iniciando..." // Set the minimum display time to 2 seconds MINIMUM_DISPLAY_TIME=2000; } // Override to return true so progress bar appears during initialization. override protected function showDisplayForInit(elapsedTime:int, count:int):Boolean { return true; } // Override to return true so progress bar appears during download. override protected function showDisplayForDownloading(elapsedTime:int, event:ProgressEvent):Boolean { return true; } //cambiamos el color de fuente de la precarga override protected function get labelFormat():TextFormat{ var tf:TextFormat=new TextFormat(); tf.color=0xFFFFFF; tf.font = "Verdana"; tf.size = 10; return tf; } override protected function createChildren(): void { super.createChildren(); } //una vez ha cargado la aplicacion cargamos el tema usando la misma precarga override protected function completeHandler(event:Event):void{ this.label="Cargando tema..."; loader=new Loader(); loader.load(new URLRequest("css/obsidian.swf")); loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, progressHandler); loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaderCompleteHandler); } private function loaderCompleteHandler(event:Event):void{ _preloader.addEventListener(FlexEvent.INIT_PROGRESS, initProgressHandler); dispatchEvent( new Event( Event.COMPLETE ) ); } override public function set preloader( preloader:Sprite ):void{ _preloader=preloader; preloader.addEventListener(ProgressEvent.PROGRESS, progressHandler); preloader.addEventListener(Event.COMPLETE, completeHandler); preloader.addEventListener(RSLEvent.RSL_PROGRESS, rslProgressHandler); preloader.addEventListener(RSLEvent.RSL_COMPLETE, rslCompleteHandler); preloader.addEventListener(RSLEvent.RSL_ERROR, rslErrorHandler); } } }
En el constructor de la clase configuramos las etiquetas en nuestro idioma. Aprovechamos para sobreescirbir el método labelFormat de manera que podamos modificar la fuente de las etiquetas para acomodar la precarga a nuestro diseño.
Lo importante llega al sobreescribir el método set preloader. Lo principal es capturar el evento Event.COMPLETE, que se disparará cuando termine la precarga y antes de que se inicie la aplicación. Cuando se dispare este evento lanzaremos la carga de nuestro CSS pasando al mismo preloader el progreso de carga de los estilos, de manera que reaprovechamos la misma precarga. Cuando termine de cargar el CSS será cuando lancemos manualmente el evento Event.COMPLETE del componente, lo cual iniciará la aplicación teniendo ya nuestros estilos en caché.
Este método no sirve sólo para cargar los estilos, podemos precargar cualquier archivo que necesitemos. Nosotros lo hemos utilizado para cargar, además de los estilos, un archivo de configuración XML. Puedes, además, ir cambiando la etiqueta de carga: cargando configuración, cargando tema, etc.
Recuerda que para jugar a extender componentes lo más sencillo siempre es ver como está construído el original. Haciendo ctrl+click en el código sobre un tipo se nos abrirá automáticamente el código fuente del mismo, pudiendo explorar los métodos que tienes disponibles, los que puedes sobreescribir y el funcionamiento completo.
Redimensionando GIF’s desde PHP I
Como desarrollador de tecnologías para móviles, uno de los servicios que hacemos habitualmente es la venta de imágenes, fondos, wallpapers o como queráis llamarlo. Este servicio requiere el escalado de los archivos para adaptarlo al tamaño de pantalla del teléfono del cliente. Además de para la descarga de contenidos, el redimensionamiento de imágenes es necesario también para adaptar el look&feel de los sites al dispositivo del cliente. En un primer momento lo hacíamos con Imagemagick, pero, por alguna extaña razón, a medida que aumentaban las visitas se saturaba el Apache de las máquinas (Windows) puesto que, parece ser, el convert no finalizaba los procesos, y había que reiniciar los servidores web. La solución fue reemplazar el sistema basado en Imagemagick por otro basado en GD, utilizando las funciones nativas de PHP, de manera que todo quedaba integrado en la propia aplicación.
Os estaréis preguntando cual es el problema en el escalado de imágenes con GD. Ninguno, el resultado era perfecto hasta que nos encontramos con gif’s transparentes. Al hacer el escalado se perdía la transparencia sin remedio. Dimos muchas vueltas y probamos distintos métodos que fuimos leyendo por cientos y cientos de foros y blogs, puesto que en la documentación de PHP no quedaba nada claro cómo hacerlo hasta que dimos con la solución.
He decidido separar este artículo en dos partes. En el segundo trataremos el escalado de gif’s animados, un proceso bastante más complejo que los estáticos.
En un GIF, la transparencia viene definida por un color, un solo color, que se define como transparente, de manera que cada pixel que tenga exactamente ese color será transparente, de ahí que, tradicionalmente, se comience por una imagen de fondo blanco o negro y sobre ella se dibuje, guardando ese color de fondo inicial como el de transparencia. La transparencia es una característica de la imagen, no del color en sí. No tiene por qué ser blanco o negro, cualquier color puede ser el transparente, pero una vez definido, cualquier pixel que tenga ese color será transparente. Por otro lado, en un GIF se guarda la paleta de colores que utiliza, de manera que se optimiza el tamaño de la imagen manteniendo la información únicamente de los colores disponibles y referenciándolos por un índice dentro de esa paleta. Esto que puede parecer un dato sin importancia es fundamental a la hora de tener claro el proceso a seguir desde PHP.
Hay varias funciones de PHP que hacen referencia a la transparencia y a los colores. En unas se utilizan los colores como tal en formato RGB, en otras se utiliza el índice de color en la paleta y en otras se necesita la representación del color. Aquí está uno de los problemas, ¿cómo se debe utilizar el resultado de cada una de las funciones disponibles?.
Lo primero que debemos calcular es el porcentaje de escalado que vamos a aplicar. Esto dependerá de nuestra aplicación y del destino de la misma. Si queremos fijar un determinado ancho habrá que calcular el porcentaje sobre el que debemos escalar el alto y viceversa. No entraremos en detalles sobre este aspecto puesto que se sale fuera del objeto de este artículo.
Una vez tenemos claro el nuevo tamaño, debemos, antes de nada, saber cual es el color transparente de la imagen original. Para ello usamos la función imagecolortransparent, si hay transparencia nos devolverá el identificador de ese color. A partir del índice del color transparente, obtenemos la representación RGB en la imagen original con imagecolorsforindex y ya podemos localizar ese color en la nueva imagen con imagecolorallocatealpha. Ya tenemos todo lo necesario. Ahora simplemente rellenamos la nueva imagen con ese color, le decimos que ese color será el transparente y copiamos la imagen original en la nueva escalando según necesitamos.
A la hora de copiar la imagen y escalarla no utilizamos imagecopyresampled ya que, a diferencia de imagecopyresized, hace una interpolazión para suavizar el resultado final. Esto, que en general es correcto, en el gif transparente provoca la deformación de pixeles propios de la interpolación, haciendo que se pierda parte de la trasparencia ya que estos pixeles no son del color exacto.
Este sería el código final.
$srcImage=imagecreatefromgif("demo.gif"); $new_w=160; $new_h=114; $dst_img=imagecreatetruecolor($new_w,$new_h); $transcolor=imagecolortransparent($srcImage); if($transcolor!=-1){ $trnprt_color = imagecolorsforindex($srcImage, $transcolor); $trnprt_indx = imagecolorallocatealpha($dst_img, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue'], $trnprt_color['alpha']); imagefill($dst_img, 0, 0, $trnprt_indx); imagecolortransparent($dst_img, $trnprt_indx); } imagecopyresized($dst_img,$srcImage,0,0,0,0,$new_w,$new_h,ImageSX($srcImage),ImageSY($srcImage));
Para muestra, un color. Con esta imagen de fondo transparente (sí, vale que no es un Picasso, pero para el ejemplo sirve :P):
Conseguimos esto:
¿Cómo que falta personal técnico?
Hace unas semanas, Rodolfo Carpintier expresaba su opinión (e insistía) sobre los profesionales del sector informático. Antes lo hizo Enrique Dans aquí y aquí.
Yo, que pertenezco a ese selecto grupo de privilegiados de los que llegan a afirmar que no conocen el paro, no puedo más que sentir indignación. Trabajo en este sector desde 2000 y he visto de todo, incluídas las colas del INEM. Tal como indica Rodolfo, yo soy de los que se creyeron muchos de esos proyectos y terminaron en esas colas.
Ahora bien, cuando hablan de que nos sobra trabajo se refieran a esos de 850 euros mensuales, bueno vale, o 900 (en 12 pagas). Así claro que no hay paro, pero también podemos prostituirnos, bueno, yo lo dudo :). De ahí a los 30.000 euros anuales que comentan creo que hay un largo trecho, y más teniendo en cuenta que hay más país mas allá de Madrid y Barcelona. Yo vivo en Valencia y no he visto una oferta de programador por ese salario nunca, acaso alguna oferta de analista con mucha experiencia.
También habla Rodolfo acerca de startups y equipos de trabajo de horas y horas sin apenas sueldo, a cambio de participaciones en el proyecto. Supongamos que me creo el proyecto y que estoy dispuesto a ello. ¿De qué se supone que voy a vivir los próximos 12 o 24 meses? ¿De lo que he ahorrado los dos años anteriores? Me invade la risa, pero a mi hipoteca todavía más.
Me hace mucha gracia que hablen de que los profesionales con experiencia considerable son cada vez más inalcanzables. ¿Acaso los buenos economistas no lo son? ¿Y los abogados? ¿Y los gestores? Cualquiera de ellos probablemente estará por encima de los buenos técnicos en materia de sueldo.
Enrique comenta que falta personal, mucho personal, y desesperadamente. Que esto está bloqueando el desarrollo de nuevos proyectos en Internet. A esto sólo cabe una respuesta. Mira los salarios que pagan. Él piensa que no puede ser así, por que dada la escasez que existe los precios subirían. Pues no. Hasta hace menos de un año se han cubierto sin problema las demandas de trabajadores a bajos sueldos y siempre había remplazo para ellos. Pero muchos se han cansado. ¿Por qué vas a programar tu por 900 euros si el de al lado, el comercial, que hace bastante menos que tú, se lleva 1800? ¿O acaso en el periodo 2000-2007 no han salido cientos y cientos de programadores? Suma ingenierías técnicas, superiores y ciclos de grado superior. Cientos de profesionales anualmente. La mayoría trabajando por sueldos ridículos en un trabajo altamente especializado. Es indudable que, como bien argumenta Enrique, la profesión sufre de un desprestigio brutal. Pero es un desprestigio provocado por la situación laboral del sector en el periodo 2001-2007.
Todos hablan de la falta de compromiso del profesional y de que quieren hacer sus 8 horas y cobrar su sueldo. ¿A alguien se le ha ocurrido pensar en el compromiso de la empresa para con el trabajador? Osea, queremos pagarles poco y que se comprometan. La empresa para qué se va a comprometer, ya le da trabajo, ¿qué mas quiere?. Pues quiero salir a las 5 de la tarde. Quiero que no me programes una reunión a las 6 de la tarde. Quiero que pienses que soy una persona y necesito mi tiempo libre, que trabajo por necesidad, porque necesito el dinero para vivir. Quiero que entiendas que, si yo estoy contento, mi trabajo será mucho mejor y estaré mas comprometido e integrado y aceptaré más retos y responsabilidades. ¿No lo ves? Además, ahora quieren profesionales comprometidos, que sepan reaccionar y con capacidad de solucionar problemas. Pero, por otro lado, una inmensa mayoría quieren programadores mandados, tú tienes que hacer esto, tú aquello y el otro lo de más allá y lo tenéis que hacer así. A picar código y me avisas cuando termines. Me quedo con esta frase de Enrique Dans:
Y es que pasar de obrero a arquitecto no sólo requiere un nivel superior de cualificación. Supone, además, que existan incentivos para ello.
Ahora está muy complicado el mercado, eso es indudable. Nuestros últimos procesos de selección se han alargado meses y meses y seguimos sin encontrar gente válida. Ya da igual que busques gente con experiencia o comprometida, es que apenas encuentras gente de ningún tipo. Es el precio que nos toca pagar de años de maltrato al profesional.
Personalmente creo que la discusión no debería ser si hay o no gente cualificada sino ¿dónde están todos los profesionales graduados los últimos años? Sencillamente resignados. El que más y el que menos ha pasado por varios trabajillos de esos de sueldos míseros. Para el que se ha pasado estudiando hasta los 23 o 25 años (en el mejor de los casos) es algo frustrante ver que no hay salida. Se han resignado y prefieren un trabajo de esos que les permite vivir tranquilamente y sin agobios esas 8 horas por un sueldo mediocre porque se han dado cuenta que no encontrarán algo mejor, y aunque lo hagan llegan al momento de creerse milongas, aún recuerdo la última vez que oí hablar de incentivos. La situación es el clásico más vale malo conocido que bueno por conocer.
Por favor, dejémonos de quejarnos de una vez. El personal técnico es, en general, gente muy preparada, capaz, profesional y, lo más importante, les encanta su trabajo. Tratémosla como tal y démosle lo que se merece. A fin de cuentas, tal como dicen todos estos gurús, en sus manos está el desarrollo de tu negocio. Recuperemos el valor de nuestra profesión.
Otro día hablaremos de la parte de culpa de las cárnicas consultoras, de las subcontratas y de la situación del sector en las Pymes, mayoría de empresas en este país.
Ajax vs. Flex
O Flex vs. Ajax. O Silverlight. O JavaFX. Menudo debate. A muchos no les gustará y sé que generará mucha controversia. Yo hablaré de Flex pues es el que conozco, pero podríais hacer la misma comparación con los otros dos.
Para el que a estas alturas no lo sepa, FLEX es la tecnología Flash orientada a programadores. Tradicionalmente Flash ha sido una herramienta de animación y diseño, la que conoces de toda la vida. Después de un primer intento más o menos fallido con Flex1 y 1.5, Adobe, tras adquirir Macromedia, decidió reorientar el rumbo, construyendo algo auténticamente revolucionario con Flex2. Las secuelas, además de Flex3, han sido AIR (para aplicaciones de escritorio) y próximamente Thermo (diseño de R.I.A.). Con Flex2 han conseguido crear una herramienta para la construcción de interfaces de usuario en Flash.
No cabe duda que las R.I.A. están de moda. Da igual la tecnología utilizada, cada día aumentan las aplicaciones online. Desde sistemas operativos online hasta aplicaciones de edición de fotografías o vídeos pasando por aplicaciones corporativas de gestión de cualquier tipo.
Pero ¿qué es mejor para construirlas? La respuesta es sencilla: depende de para qué. No me sirven argumentos sobre plugins (¿Javascript funciona en Lynx?) o software libre vs. privativo (¿acaso al usuario habitual le importa o sabe lo que es?) o SEO (¿Javascript es search engine friendly?). Hablemos mejor de utilidad y de conveniencia para el desarrollador y el usuario.
Lo primero que deberíamos preguntarnos es
¿Qué voy a hacer?
No es lo mismo hacer una web o una aplicación que va a utilizar mucha gente que hacer una aplicación de gestión para una empresa de seguros o un banco. ¿Alguien se imagina a una aseguradora haciendo su software de gestión en Javascript? Pero sí en Flex como de hecho están haciendo ya. Por otro lado a nadie se le ocurriría hacer una web en Flex, no tiene sentido, no es su cometido. Pero sí que harías una aplicación como la de Flickr para editar fotografías online o la de Youtube para hacer montajes de vídeo. O pequeños módulos concretos que no podrías hacer de otro modo o que te costaría demasiado, o widgets varios como está haciendo mucha gente utilizando feeds, mapas, etc. Este creo que sería el punto clave diferenciador. Con Ajax hacemos complementos para aplicaciones web o pequeñas aplicaciones, con Flex hacemos aplicaciones completas. Lógicamente estoy generalizando y cada uno puede pensar y hacer lo que quiera, hay aplicaciones completas realizadas 100% en Javascript, no hay ningun inconveniente, para ejemplo EyeOS. ¿Por qué opino esto entonces? Sencillo, por una simple cuestión de ingeniería del software y productividad. Flex es un lenguaje orientado a objetos 100% y con todas las ventajas que aporta. Crear interfaces de usuario con Flex es impresionantemente sencillo. ¿Alguien puede decir lo mismo de Javascript? Que se puede hacer es indudable, pero a costa de convertir la aplicación en una auténtica telaraña de Javascript‘s. ¿Qué ocurrirá cuando otro programador deba tomar esa aplicación y modificarla? Cualquiera que haya hecho lo más mínimo en Ajax sabe que es una locura. No hay un patrón MVC y la interacción con la interfaz de usuario (html) es harto difícil, innerHTML está muy bien y es muy rápido, pero siendo puristas deberíamos utilizar DOM, a medio plazo lo agradeceremos, y sino intenta modificar atributos de código insertado con innerHTML :P.
Mejor aún, encontremos un responsable de proyectos, director técnico o el cargo que se os ocurra que se comprometa a realizar un proyecto medianamente importante en Ajax. Si conoce Flex verá las similitudes con Java, de hecho se hizo con Java en mente. Si no lo conoce pensará directamente en Java, difícilmente se le ocurriría pensar en Ajax, su cuello es el que está en juego en definitiva.
¿Para qué hemos utilizado nosotros Flex?
Hemos hecho widgets de distintos tipos, paneles de control y gestión, aplicaciones de audio/vídeo multiusuario (chat, audio-chat y vídeo-chat). Ahora mismo trabajamos en un cliente IRC en colaboración con una de las principales redes de IRC.
En backoffices y otros paneles administrativos hemos comprobado que para el usuario la comprensión y utilización de la aplicación es muy superior a interfaces html puesto que son más parecidas a una aplicación de escritorio tradicional y tienen más interactividad, algo que el usuario agradece.
En artículos posteriores os expondré algunos ejemplos de cosas que hemos ido haciendo a lo largo de los dos últimos años, aunque también veremos cosas en Ajax, no son tecnologías excluyentes.
Añadir que Flex ya es de código abierto, el SDK es libre y hay un excelente plugin para Eclipse. Lo único que es de pago es el Flex Builder de Adobe, la aplicación oficial, pero puedes hacer tus aplicaciones con el plugin de Eclipse del mismo modo.
Por cierto, Adobe no me paga nada por este post :P.
¿Por qué nos siguen enseñando gestión de proyectos en cascada?
Cada día que pasa lo entiendo menos. Cualquiera que haya participado en algún proyecto de software, da igual el tamaño del mismo, se habrá dado cuenta que los patrones a seguir que nos enseñan a todos en las asignaturas de ingeniería del software y gestión de proyectos ya no tienen sentido. De hecho hace mucho tiempo que dejaron de tenerlo. Toma de requisitos, análisis, desarrollo, pruebas, implantación… Y cuando los usuarios finales comienzan a utilizarlo te das cuenta de que la usabilidad es nula (no es lo mismo como piensan los técnicos que los usuarios finales), faltan funcionalidades básicas, determinados procedimientos cotidianos se han complicado tanto que el trabajo diario se alarga… ¿Solución? O bien se aumenta el presupuesto para contemplar las modificaciones (suele ocurrir con las administraciones públicas) o bien el cliente no te paga hasta que el software funcione como él y sus empleados necesitan, con lo cual tu estimación de tiempo/recursos se ha quedado corta y empiezas a perder dinero sobre el plan inicial.
¿Por qué ocurre esto? Sencillo, por que es muy difícil contemplar todas las necesidades del cliente en la fase inicial del proyecto. Pensar que con las notas que se toman durante esas semanas va a ser suficiente lleva a ocultar las potenciales carencias. Sí, hablas con el personal de tu cliente, ves como trabajan, te lo apuntas todo. Pero tú no eres el que trabajará con la aplicación, son ellos, pero vas a plantear el proyecto desde tu punto de vista, no desde el del cliente. Cuando les enseñas el producto aparecen los problemas.
¿Y si, en vez de eso, implantas en tu equipo de desarrollo técnicas de desarrollo ágil? Podríamos decir que sería un método de desarrollo orientado a producto, periódicamente se liberan versiones funcionales del software, con más o menos funcionalidades, pero que funcionan y permiten al cliente toquetear y jugar con su compra.
El ejemplo más evidente lo tenemos en el desarrollo de webs. Cualquiera que haya trabajado en ese campo se habrá topado con el momento en que el cliente empieza a ver lo que hay hecho (es una web, puede verla en cualquier momento ¿no?) y comienza a pedir cambios y a decir cosas que no le gustan. Para nosotros, que tenemos nuestra idea clara del desarrollo lineal, que sabemos que teníamos tres meses para hacer el desarrollo y vamos a cumplir ese plazo, comienzan los problemas. Pierdes la línea de tus planes y comienzas a centrarte en lo que el cliente ha dicho al ver cosas, en cambios, en errores, olvidándote de todo lo planificado que te queda por hacer porque mañana volverá a ver la web y querrá ver los cambios solicitados, tu mente en cascada no es capaz de verlo de otro modo.
En vez de eso vamos planteando pequeños desarrollos cada tres o cuatro semanas. Cada periodo se presenta algo nuevo que funciona, es decir, comenzamos por una pequeña porción del sistema, siempre funcional, y la vamos aumentando poco a poco en periodos posteriores teniendo siempre en cuenta los errores detectados y las impresiones del cliente, sin olvidarse de las funcionalidades pendientes. Estaríamos, por tanto, ante un desarrollo adaptativo en función de las necesidades que se van viendo sobre la marcha. Por esta misma razón comenzamos a tirar líneas de código mucho antes que en los procesos tradicionales ya que apenas nos preocupamos por concretar todos los detalles al inicio, lo vamos haciendo sobre la marcha y con una idea inicial bastante general es suficiente. El objetivo es algo siempre complicado, que el cliente se implique en el proyecto y aporte continuamente su feedback.
Puedes comenzar con el Manifiesto ágil y buscar documentación y bibliografía sobre desarrollo ágil y SCRUM (una de las técnicas más utilizadas). Ya de paso no estaría de más echar un vistazo a Xtreme Programing (no, no es trabajar 15 horas diarias).
A los desarrolladores, en general, nos cuesta mucho asimilar los conceptos ágiles. Nos enseñaron a ser mecánicos en los procedimientos de desarrollo de software y este tipo de técnicas se basan más en ir sobre la marcha, y cuidado, esto no significa ni mucho menos que sin planificación, al contrario. La primera pregunta del director de proyectos la primera vez que oye hablar de desarrollo ágil será: bueno, pero así ¿cómo hago las estimación del tiempo necesario? Pues igual que lo hacías hasta ahora, estamos proponiendo nuevas técnicas de desarrollo, pero la estimación tienes que seguir haciéndola igual: piensa en las tareas que hay que hacer y estima el tiempo necesario para realizarlas, compáralo con la cantidad de recursos (personas) de tu equipo y obtén las estimación, o mejor aún, ya que hablamos de técnicas ágiles, deja que sea el propio equipo el que estime la duración de las tareas, a fin de cuentas son los que las van a hacer.