<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://www.victorvelarde.com">
  <title>velardev</title>
  <link href="https://www.victorvelarde.com/feed.xml" rel="self"/>
  <link href="https://www.victorvelarde.com"/>
  <updated>2026-11-06T11:06:34Z</updated>
  <id>https://www.victorvelarde.com</id><entry>
    <title>Promptly 07 - Perfil y licencia</title>
    <updated>2026-11-06T11:06:25Z</updated>
    <id>https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-07-perfil-y-licencia/</id>
    <content type="html">&lt;div class=&quot;transclusion internal-embed is-loaded&quot;&gt;&lt;div class=&quot;markdown-embed&quot;&gt;
&lt;p&gt;&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-01-el-arranque-del-proyecto/&quot;&gt;Promptly 01 - El arranque del proyecto&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-02-listar-prompts/&quot;&gt;Promptly 02 - Listar prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-03-ejecutar-un-prompt/&quot;&gt;Promptly 03 - Ejecutar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-04-publicar-un-prompt/&quot;&gt;Promptly 04 - Publicar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-05-importacion-de-prompts/&quot;&gt;Promptly 05 - Importación de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-06-busqueda-de-prompts/&quot;&gt;Promptly 06 - Búsqueda de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-07-perfil-y-licencia/&quot;&gt;Promptly 07 - Perfil y licencia&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code class=&quot;language-table-of-contents&quot;&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;sesion-7&quot; tabindex=&quot;-1&quot;&gt;Sesión 7&lt;/h1&gt;
&lt;h2 id=&quot;punto-de-partida&quot; tabindex=&quot;-1&quot;&gt;Punto de partida&lt;/h2&gt;
&lt;h3 id=&quot;objetivos&quot; tabindex=&quot;-1&quot;&gt;Objetivos&lt;/h3&gt;
&lt;p&gt;El producto ya tiene un valor, desde mi punto de vista. Le podríamos agregar muchas más features, claro, pero considero que ya puede ser una herramienta sencilla y util para gestionar prompts. En este punto, creo que lo interesante antes de invertir más esfuerzo es sacarlo y recibir feedback.&lt;/p&gt;
&lt;p&gt;Solo hay un aspecto último que me gustaría añadir antes a nivel funcional, y es una pequeña página de &lt;strong&gt;perfil del usuario&lt;/strong&gt; + un selector de &lt;strong&gt;licencia&lt;/strong&gt; al publicar los prompts.  Si los usuarios quieren usar la app, aun manteniendo en privado su email, parece que tiene sentido que mantengan su pequeño espacio, que les identifique claramente como autores, y permita reconocimiento / enlaces a otros lugares que ellos manejen (por ejemplo, un enlace a una web de un profesor que escriba prompts para su asignatura).&lt;/p&gt;
&lt;p&gt;Por otro lado, la propia licencia de los prompts me parece otro aspecto importante. Muchas veces los prompts son breves, y se comparten tal cual, sin esperar reconocimiento de ningún tipo. Otras veces, sin embargo los prompts son colecciones o razonamientos mucho más elaborados, a los que los autores quieren seguir vinculados... Ambas cosas se pueden resolver con el perfil de usuario + selector licencia prompt, así que las agrego a la lista y me pongo a implementarlas, como últimas features antes de lanzar la aplicación.&lt;/p&gt;
&lt;h3 id=&quot;tareas&quot; tabindex=&quot;-1&quot;&gt;Tareas&lt;/h3&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_0&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_0&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_1&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_1&quot;&gt; Logarse después&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_2&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_2&quot;&gt; Persistir y recuperar prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_3&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_3&quot;&gt; Ejecutar un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_4&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_4&quot;&gt; Copiar un prompt al portapapeles&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_5&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_5&quot;&gt; Catálogo de prompts públicos&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_6&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_6&quot;&gt; Importación de prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_7&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_7&quot;&gt; Configuración de dominio&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_8&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_8&quot;&gt; Lista mejorada de prompts + username&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_9&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_9&quot;&gt; Colecciones de prompts con tags / buscar&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_10&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_10&quot;&gt; Compartir un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_11&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_11&quot;&gt; Perfil de usuario&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_12&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_12&quot;&gt; Licencia de prompt&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dejamos para el futuro otras ideas de features, pendiente de ver con los usuarios si son interesantes:&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_13&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_13&quot;&gt; Ajustes de prompt (idioma de salida, tono, etc.)&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_14&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_14&quot;&gt; Likes de prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_15&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_15&quot;&gt; Fork de prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_16&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_16&quot;&gt; etc.&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;acciones&quot; tabindex=&quot;-1&quot;&gt;Acciones&lt;/h2&gt;
&lt;h3 id=&quot;perfil-de-usuario&quot; tabindex=&quot;-1&quot;&gt;Perfil de usuario&lt;/h3&gt;
&lt;p&gt;Actualmente los usuarios se registran con 3 elementos: nombre de usuario, email (privado) y contraseña. Lo más flexible para dar voz (espacio en la UI) a los autores, es dejarles un espacio. Como me encanta la simplicidad y potencia del markdown (texto, enlaces, imágenes...), voy a añadir un espacio al registro que permita una pequeña &amp;quot;&lt;strong&gt;Bio&lt;/strong&gt;&amp;quot; con .md. Y de paso, voy a ofrecer la posibilidad de cambiar dicha Bio y el Username, desde un espacio para editar el perfil.&lt;/p&gt;
&lt;p&gt;Para acceder al perfil de un usuario (url tipo &lt;em&gt;/profiles/promptly&lt;/em&gt;), se puede acceder desde la vista individual del prompt. He aprovechado para agregar también la búsqueda de prompts en el catalogo de un usuario y he linkado ambas vistas. La feature de forma conjunta se ve tal que así:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2025-05-coleccion-promptly/media/promptly_09_profile.gif&quot; alt=&quot;promptly_09_profile.gif&quot; /&gt;&lt;br /&gt;
🔝para el render he usado la librería &lt;code&gt;react-markdown&lt;/code&gt; , que siempre me ha dado buenos resultados.&lt;/p&gt;
&lt;h3 id=&quot;licencia-de-prompts&quot; tabindex=&quot;-1&quot;&gt;Licencia de prompts&lt;/h3&gt;
&lt;p&gt;De nuevo, nos podríamos complicar aquí, pero vamos a ir una lista cerrada de licencias: las Creative Commons (CC), que me parecen las más adecuadas para prompts. Y para cubrir casos especiales, simplemente agregaremos una opción cajon de sastre &amp;quot;Otra&amp;quot;, donde el usuario pueda escribir lo que desee (por ejemplo &amp;quot;MIT&amp;quot;, pero en modo texto abierto, para simplificar).&lt;/p&gt;
&lt;p&gt;La selección de tipo de licencia, desde la edición de prompts (modo privado) se ve tal que así:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2025-05-coleccion-promptly/media/promptly_10_license.gif&quot; alt=&quot;promptly_10_license.gif&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;resumen&quot; tabindex=&quot;-1&quot;&gt;Resumen&lt;/h2&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_17&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_17&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_18&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_18&quot;&gt; Logarse después&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_19&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_19&quot;&gt; Persistir y recuperar prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_20&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_20&quot;&gt; Ejecutar un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_21&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_21&quot;&gt; Copiar un prompt al portapapeles&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_22&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_22&quot;&gt; Catálogo de prompts públicos&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_23&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_23&quot;&gt; Importación de prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_24&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_24&quot;&gt; Configuración de dominio&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_25&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_25&quot;&gt; Lista mejorada de prompts + username&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_26&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_26&quot;&gt; Colecciones de prompts con tags / buscar&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_27&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_27&quot;&gt; Compartir un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_28&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_28&quot;&gt; Perfil de usuario&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_29&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_29&quot;&gt; Licencia de prompt&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Vamos a sacar &lt;a href=&quot;https://promptly.es/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://promptly.es&lt;/a&gt; y ver cómo se desarrolla 😄&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-07-perfil-y-licencia/"/>
  </entry><entry>
    <title>Promptly 06 - Búsqueda de prompts</title>
    <updated>2026-11-06T11:06:25Z</updated>
    <id>https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-06-busqueda-de-prompts/</id>
    <content type="html">&lt;div class=&quot;transclusion internal-embed is-loaded&quot;&gt;&lt;div class=&quot;markdown-embed&quot;&gt;
&lt;p&gt;&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-01-el-arranque-del-proyecto/&quot;&gt;Promptly 01 - El arranque del proyecto&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-02-listar-prompts/&quot;&gt;Promptly 02 - Listar prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-03-ejecutar-un-prompt/&quot;&gt;Promptly 03 - Ejecutar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-04-publicar-un-prompt/&quot;&gt;Promptly 04 - Publicar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-05-importacion-de-prompts/&quot;&gt;Promptly 05 - Importación de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-06-busqueda-de-prompts/&quot;&gt;Promptly 06 - Búsqueda de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-07-perfil-y-licencia/&quot;&gt;Promptly 07 - Perfil y licencia&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code class=&quot;language-table-of-contents&quot;&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;sesion-6&quot; tabindex=&quot;-1&quot;&gt;Sesión 6&lt;/h1&gt;
&lt;h2 id=&quot;punto-de-partida&quot; tabindex=&quot;-1&quot;&gt;Punto de partida&lt;/h2&gt;
&lt;h3 id=&quot;objetivos&quot; tabindex=&quot;-1&quot;&gt;Objetivos&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Sin cambios&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;tareas&quot; tabindex=&quot;-1&quot;&gt;Tareas&lt;/h3&gt;
&lt;p&gt;Como ya señalamos en sesiones previas, el manejo de tags parece un elemento importante, lo trabajaremos en esta sesión. Sin embargo, vista la experiencia actual del listado de prompts, no parece que  tenga sentido mostrar toda la info y las acciones en el listado de prompts (por claridad, por rapidez en las consultas...). Sería mejor mostrar esa info desde el detalle del prompt individual; así que voy a hacer primero algunos ajustes, simplificando la lista de prompts.&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_0&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_0&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_1&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_1&quot;&gt; Logarse después&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_2&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_2&quot;&gt; Persistir y recuperar prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_3&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_3&quot;&gt; Ejecutar un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_4&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_4&quot;&gt; Copiar un prompt al portapapeles&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_5&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_5&quot;&gt; Catálogo de prompts públicos&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_6&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_6&quot;&gt; Importación de prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_7&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_7&quot;&gt; Configuración de dominio&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_8&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_8&quot;&gt; Lista mejorada de prompts + username&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_9&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_9&quot;&gt; Colecciones de prompts con tags&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_10&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_10&quot;&gt; Compartir un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_11&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_11&quot;&gt; Ajustes de prompt (idioma de salida, tono, etc.)&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;acciones&quot; tabindex=&quot;-1&quot;&gt;Acciones&lt;/h2&gt;
&lt;h3 id=&quot;lista-mejorada-de-prompts-username&quot; tabindex=&quot;-1&quot;&gt;Lista mejorada de prompts + username!&lt;/h3&gt;
&lt;p&gt;Solo &lt;em&gt;nombre, autor y fecha del prompt&lt;/em&gt; en la lista sería lo ideal; solo hay un pequeño problema 😄: con el registro actual (email y clave), no hay nombre de usuario, solo email.&lt;/p&gt;
&lt;p&gt;En este punto, creo que es razonable que se pida como input y se guarde al registrar al usuario, como el típico &lt;em&gt;username&lt;/em&gt; único de toda red social. No creo que sea adecuado mostrar, por privacidad, el email de las personas que se registran; este solo es requerido para validar el signup / sign-in y si lo compartes, puede acabar en spam o correos no deseados.&lt;/p&gt;
&lt;p&gt;Tiro un poco de ChatGPT, lo suficiente para averiguar que Supabase no da la feature tal cual, hay que currárselo un poco y saber lo que se quiere (precisamente porque exponer email u otros datos de los users es delicado). Me tropiezo con esta &lt;a href=&quot;https://stackoverflow.com/questions/78550922/how-do-i-authorise-users-with-username-in-supabase&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;referencia&lt;/a&gt;, que a su vez apunta a la guia recomendada de &lt;strong&gt;Supabase&lt;/strong&gt;  para &lt;a href=&quot;https://supabase.com/docs/guides/auth/managing-user-data&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;user-data&lt;/a&gt; y sigo su enfoque: básicamente un trigger y una nueva tabla para mantener users y sus nombres separados pero sincronizados.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!info]&lt;br /&gt;
Por cierto, me he dado cuenta, al ir a mirar unos cambios WIP, que no tengo debugger para el lado server-side (sí para cliente, con chrome). Siguiendo la guía de NextJS se pueden poner ambos en VS Code: &lt;a href=&quot;https://nextjs.org/docs/pages/guides/debugging#debugging-with-vs-code&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://nextjs.org/docs/pages/guides/debugging#debugging-with-vs-code&lt;/a&gt;, y con esto en el proyecto es más fácil trabajar.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Esta es la vista simplificada:&lt;br /&gt;
&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2025-05-coleccion-promptly/media/promptly_07_simple_public_list.png&quot; alt=&quot;promptly_07_simple_public_list.png&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;colecciones-de-prompts-con-tags-buscar&quot; tabindex=&quot;-1&quot;&gt;Colecciones de prompts con tags / buscar&lt;/h3&gt;
&lt;p&gt;Las tags son un elemento central, pero en compañía de estas y para tener un buen catálogo, hay ciertos elementos que sí o sí deberían estar en el producto, así que con algo de ayuda de ChatGPT y GPT-4.1 en VSCode, implemento las siguientes features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1. paginación&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2. búsqueda por título&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3. búsqueda por tag&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;4. ordenación&lt;/strong&gt; (asc / desc, por título y fecha)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;ux-de-la-busqueda&quot; tabindex=&quot;-1&quot;&gt;UX de la búsqueda&lt;/h4&gt;
&lt;p&gt;Como el objetivo al final es poder compartir fácilmente prompts, es interesante &lt;em&gt;persistir el estado en la url&lt;/em&gt;, lo cual permite compartir búsquedas fácilmente...&lt;/p&gt;
&lt;p&gt;Durante un rato le he estado dando vueltas también a la experiencia de uso de los tags. Por ejemplo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;he considerado que la búsqueda por tag no sea exacta, sino case insensitive&lt;/li&gt;
&lt;li&gt;distintas formas de seleccionar las tags por UI&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Después de revisar experiencias de otros productos, he decidido adoptar un enfoque que me gusta mucho y es sencillo, que son las &lt;em&gt;labels en github&lt;/em&gt;: básicamente en la caja de búsqueda se permite escribir una sintaxis muy sencilla (label:nombreLabel) y esto permite de forma cómoda agregarlas a la búsqueda; en mi caso lo implemento, con tag:miTag. Este mismo sistema es extrapolable a otras propiedades en el futuro (por ejemplo, búsqueda por usuario, con user:username).&lt;/p&gt;
&lt;p&gt;Por otro lado, para aplicarlas, basta con hacer clic en la tag, cuando aparecen asignadas (permitiendo algo como &amp;quot;buscar similares&amp;quot;): caja de búsqueda - url irán siempre acompasadas. Para el filtro, juego un rato con la UI y me decido por un dropdown sencillo, a la derecha de la caja de búsqueda.&lt;/p&gt;
&lt;p&gt;La experiencia de este bloque se ve algo como:&lt;br /&gt;
&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2025-05-coleccion-promptly/media/promptly_08_tags_search.gif&quot; alt=&quot;promptly_08_tags_search.gif&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;compartir-prompt&quot; tabindex=&quot;-1&quot;&gt;Compartir prompt&lt;/h3&gt;
&lt;p&gt;Una vez la búsqueda está resuelta bien, la página clave es la &lt;em&gt;vista pública del prompt&lt;/em&gt;. Una página sencilla, de solo lectura, para mostrar el prompt en sí, que implemento de forma rápida. La url será /prompts/[id_prompt]&lt;/p&gt;
&lt;p&gt;Como está pensada como página pública, para compartir un prompt por ejemplo en una red social, es interesante dotarla de los metadatos en el servidor, basados en un estándar como &lt;em&gt;OpenGraph&lt;/em&gt;. NextJS permite fácilmente usar su función &lt;em&gt;generateMetadata&lt;/em&gt; para que se generen las previsualizaciones adecuadas al compartir.&lt;/p&gt;
&lt;h2 id=&quot;resumen&quot; tabindex=&quot;-1&quot;&gt;Resumen&lt;/h2&gt;
&lt;p&gt;El producto va teniendo forma... Se incluye:&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_12&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_12&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_13&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_13&quot;&gt; Logarse después&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_14&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_14&quot;&gt; Persistir y recuperar prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_15&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_15&quot;&gt; Ejecutar un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_16&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_16&quot;&gt; Copiar un prompt al portapapeles&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_17&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_17&quot;&gt; Catálogo de prompts públicos&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_18&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_18&quot;&gt; Importación de prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_19&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_19&quot;&gt; Configuración de dominio&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_20&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_20&quot;&gt; Lista mejorada de prompts + username&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_21&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_21&quot;&gt; Colecciones de prompts con tags / buscar&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_22&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_22&quot;&gt; Compartir un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_23&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_23&quot;&gt; Ajustes de prompt (idioma de salida, tono, etc.)&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-06-busqueda-de-prompts/"/>
  </entry><entry>
    <title>Promptly 05 - Importación de prompts</title>
    <updated>2026-11-06T11:06:25Z</updated>
    <id>https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-05-importacion-de-prompts/</id>
    <content type="html">&lt;div class=&quot;transclusion internal-embed is-loaded&quot;&gt;&lt;div class=&quot;markdown-embed&quot;&gt;
&lt;p&gt;&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-01-el-arranque-del-proyecto/&quot;&gt;Promptly 01 - El arranque del proyecto&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-02-listar-prompts/&quot;&gt;Promptly 02 - Listar prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-03-ejecutar-un-prompt/&quot;&gt;Promptly 03 - Ejecutar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-04-publicar-un-prompt/&quot;&gt;Promptly 04 - Publicar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-05-importacion-de-prompts/&quot;&gt;Promptly 05 - Importación de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-06-busqueda-de-prompts/&quot;&gt;Promptly 06 - Búsqueda de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-07-perfil-y-licencia/&quot;&gt;Promptly 07 - Perfil y licencia&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code class=&quot;language-table-of-contents&quot;&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;sesion-5&quot; tabindex=&quot;-1&quot;&gt;Sesión 5&lt;/h1&gt;
&lt;h2 id=&quot;punto-de-partida&quot; tabindex=&quot;-1&quot;&gt;Punto de partida&lt;/h2&gt;
&lt;h3 id=&quot;objetivos&quot; tabindex=&quot;-1&quot;&gt;Objetivos&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;No cambian.&lt;/li&gt;
&lt;li&gt;Sin embargo, conforme trabajo en el proyecto, más claro tengo que si quiero que sea realmente útil tiene que tener algunos buenos contenidos de origen, es decir, un catálogo de prompts base, aunque luego la gente vaya agregando los suyos. Por esto mismo, he decidido buscar e importar prompts al sistema, antes de avanzar más en nuevas features, y agrego esto a la lista de tareas.&lt;/li&gt;
&lt;li&gt;Hasta ahora he utilizado las urls &#39;autogeneradas&#39; / aka feas pero gratuitas de Vercel. Pero está claro que un dominio propio es preferible. Agrego la tarea de configuración de dominio a mi lista.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;tareas&quot; tabindex=&quot;-1&quot;&gt;Tareas&lt;/h3&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_0&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_0&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_1&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_1&quot;&gt; Logarse después&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_2&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_2&quot;&gt; Persistir el prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_3&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_3&quot;&gt; Ejecutar un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_4&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_4&quot;&gt; Copiar un prompt al portapapeles&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_5&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_5&quot;&gt; Catálogo de prompts públicos&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_6&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_6&quot;&gt; Importación de prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_7&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_7&quot;&gt; Configuración de dominio&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_8&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_8&quot;&gt; Compartir un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_9&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_9&quot;&gt; Colecciones de prompts con tags&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_10&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_10&quot;&gt; Ajustes de prompt (idioma de salida, tono, etc.)&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;acciones&quot; tabindex=&quot;-1&quot;&gt;Acciones&lt;/h2&gt;
&lt;h3 id=&quot;importacion-de-prompts&quot; tabindex=&quot;-1&quot;&gt;Importación de prompts&lt;/h3&gt;
&lt;p&gt;La licencia Creative Commons CC0 en &lt;em&gt;awesome-chatgpt-prompts&lt;/em&gt; permite una importación libre, y ChatGPT nos ayuda a generar un script para ello, en base a nuestras indicaciones de mapeo de campos. Me siento cómodo con el scripting en python, así que voy a ello (en este punto, aprovecho también para configurar el entorno con pyenv en local, requirements.txt y un fichero .env para las credenciales).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import pandas as pd
import psycopg2
import os

# Parámetros de conexión
conn = psycopg2.connect(
    host=&amp;quot;TU_SUPABASE_HOST&amp;quot;,
    dbname=&amp;quot;TU_BD&amp;quot;,
    user=&amp;quot;TU_USER&amp;quot;,
    password=&amp;quot;TU_PASSWORD&amp;quot;,
    port=5432,
    sslmode=&#39;require&#39;
)

cur = conn.cursor()

# Cargar CSV desde URL
df = pd.read_csv(&#39;https://raw.githubusercontent.com/f/awesome-chatgpt-prompts/refs/heads/main/vibeprompts.csv&#39;)

# Función de transformación de cada fila
def transform_row(row, user_id):
    tags = []

    # Añadir contributor como tag si existe
    if pd.notna(row.get(&#39;contributor&#39;)):
        tags.append(row[&#39;contributor&#39;].strip())

    # Añadir techstack separados por coma
    if pd.notna(row.get(&#39;techstack&#39;)):
        tech_tags = [tech.strip() for tech in row[&#39;techstack&#39;].split(&#39;,&#39;) if tech.strip()]
        tags.extend(tech_tags)

    # Añadir el extra tag &#39;vibeprompt&#39;
    tags.append(&#39;vibeprompt&#39;)

    return (
        row[&#39;app&#39;],             # title
        row[&#39;prompt&#39;],          # content
        tags,                   # tags (Postgres ARRAY)
        user_id                 # user_id
    )

# USER_ID: debes establecer tu UUID de usuario
USER_ID = &amp;quot;el-id-usuario-del-usuario-creado&amp;quot;

# Insertar cada fila transformada
for idx, row in df.iterrows():
    title, content, tags, user_id = transform_row(row, USER_ID)
    cur.execute(&amp;quot;&amp;quot;&amp;quot;
        INSERT INTO public.prompts (
            title, content, tags, user_id
        ) VALUES (%s, %s, %s, %s)
    &amp;quot;&amp;quot;&amp;quot;, (title, content, tags, user_id))

conn.commit()
cur.close()
conn.close()

print(&amp;quot;Importación completada correctamente 🚀&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Por cierto, el hecho de que Supabase sea realmente un postgres accesible, que permita una conexión directa desde mi equipo local, es sin duda una gran ventaja. Normalmente los proveedores SaaS que montan sobre una DB no conceden acceso directo, por protección / seguridad o voluntad explícita, pero no es así en este caso, y es muy de agradecer,&lt;/p&gt;
&lt;h3 id=&quot;configuracion-de-dominio&quot; tabindex=&quot;-1&quot;&gt;Configuración de dominio&lt;/h3&gt;
&lt;p&gt;La compra del dominio la realizo en &lt;a href=&quot;http://godaddy.com/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;godaddy.com&lt;/a&gt;, donde tengo otros dominios. Encuentro libre &lt;em&gt;&lt;a href=&quot;http://promptly.es/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;promptly.es&lt;/a&gt;&lt;/em&gt;, en buenas condiciones de precio, así que voy a por ello.&lt;/p&gt;
&lt;p&gt;Es sencillo y rápido de configurar el dominio con Vercel (básicamente es factible hacer una autoconfiguración desde su panel)&lt;/p&gt;
&lt;h2 id=&quot;resumen&quot; tabindex=&quot;-1&quot;&gt;Resumen&lt;/h2&gt;
&lt;p&gt;Empieza a haber contenidos interesantes (yo mismo exploro la colección y veo algunas cosas curiosas). Estado:&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_11&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_11&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_12&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_12&quot;&gt; Logarse después&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_13&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_13&quot;&gt; Persistir el prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_14&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_14&quot;&gt; Ejecutar un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_15&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_15&quot;&gt; Copiar un prompt al portapapeles&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_16&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_16&quot;&gt; Catálogo de prompts públicos&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_17&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_17&quot;&gt; Importación de prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_18&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_18&quot;&gt; Configuración de dominio&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_19&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_19&quot;&gt; Compartir un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_20&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_20&quot;&gt; Colecciones de prompts con tags&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_21&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_21&quot;&gt; Ajustes de prompt (idioma de salida, tono, etc.)&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-05-importacion-de-prompts/"/>
  </entry><entry>
    <title>Promptly 04 - Publicar un prompt</title>
    <updated>2026-11-06T11:06:25Z</updated>
    <id>https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-04-publicar-un-prompt/</id>
    <content type="html">&lt;div class=&quot;transclusion internal-embed is-loaded&quot;&gt;&lt;div class=&quot;markdown-embed&quot;&gt;
&lt;p&gt;&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-01-el-arranque-del-proyecto/&quot;&gt;Promptly 01 - El arranque del proyecto&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-02-listar-prompts/&quot;&gt;Promptly 02 - Listar prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-03-ejecutar-un-prompt/&quot;&gt;Promptly 03 - Ejecutar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-04-publicar-un-prompt/&quot;&gt;Promptly 04 - Publicar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-05-importacion-de-prompts/&quot;&gt;Promptly 05 - Importación de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-06-busqueda-de-prompts/&quot;&gt;Promptly 06 - Búsqueda de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-07-perfil-y-licencia/&quot;&gt;Promptly 07 - Perfil y licencia&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code class=&quot;language-table-of-contents&quot;&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;sesion-4&quot; tabindex=&quot;-1&quot;&gt;Sesión 4&lt;/h1&gt;
&lt;h2 id=&quot;punto-de-partida&quot; tabindex=&quot;-1&quot;&gt;Punto de partida&lt;/h2&gt;
&lt;h3 id=&quot;objetivos&quot; tabindex=&quot;-1&quot;&gt;Objetivos&lt;/h3&gt;
&lt;p&gt;Los mismos, principalmente de aprendizaje mientras se crea una herramienta sencilla pero útil.&lt;/p&gt;
&lt;h3 id=&quot;tareas&quot; tabindex=&quot;-1&quot;&gt;Tareas&lt;/h3&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_0&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_0&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_1&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_1&quot;&gt; Logarse después&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_2&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_2&quot;&gt; Persistir y recuperar prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_3&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_3&quot;&gt; Ejecutar un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_4&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_4&quot;&gt; Copiar un prompt al portapapeles&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_5&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_5&quot;&gt; Compartir un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_6&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_6&quot;&gt; Catálogo de prompts públicos&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_7&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_7&quot;&gt; Colecciones de prompts con tags&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_8&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_8&quot;&gt; Ajustes de prompt (idioma de salida, tono, etc.)&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;acciones&quot; tabindex=&quot;-1&quot;&gt;Acciones&lt;/h2&gt;
&lt;h3 id=&quot;compartir-un-prompt&quot; tabindex=&quot;-1&quot;&gt;Compartir un prompt&lt;/h3&gt;
&lt;p&gt;Todos los prompts son &lt;em&gt;privados&lt;/em&gt; por defecto, es decir que la app puede funcionar como un simple repositorio personal de prompts si se desea. Pero es innegable que compartir los prompts hará la experiencia mucho más valiosa, así que explotemos esto y desarrollemos la/s features necesarias para compartir un prompt públicamente.&lt;/p&gt;
&lt;p&gt;Tener un &lt;strong&gt;prompt público&lt;/strong&gt; se descompone en varios pasos o subtareas. Será necesario:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;Indicador de privacidad&lt;/em&gt;: mostrar claramente en la lista de prompts si un un prompt es público o privado&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Cambio de privacidad&lt;/em&gt;: permitir cambiar su privacidad fácilmente&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Vista pública de prompt&lt;/em&gt;: preparar una página sencilla, que no requiera usuario, y permita ver el detalle del prompt, incluyendo al autor
&lt;ul&gt;
&lt;li&gt;Si es público se muestra&lt;/li&gt;
&lt;li&gt;Si es privado, no funciona (404)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_9&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_9&quot;&gt; (1) Indicador de privacidad&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Aprovecho la necesidad de editar la lista de prompts para refactorizar un foco y limpiar el componente. Con ayuda de Copilot y algunos toques manuales (+ pruebas, por ejemplo con el movil conectado)... avanzo hasta tener algo como:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2025-05-coleccion-promptly/media/promptly_05_public_or_private_prompt.png&quot; alt=&quot;promptly_05_public_or_private_prompt.png&quot; /&gt;&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_10&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_10&quot;&gt; (2) Cambio de privacidad&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A golpe de prompt voy desarrollando la acción para Editar todo el prompt. Creo que es un buen primer paso: dejar editar todo en una nueva página (y no solo la privacidad en el listado, eso puede venir más adelante si es útil).&lt;/p&gt;
&lt;p&gt;Un ejemplo del tipo de prompt que uso:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-prompt&quot;&gt;Que los botones ChatGPT y Copy no floten, sino que vayan dentro de un espacio común, llamado actions o similar, dentro de cada list-item. Que a ese espacio se le agregue una nueva acción, en forma de enlace llamado Edit. Que ese Edit abra una nueva página dashboard/edit-prompt (que se agregue como const en routes.ts). Que la pagina de Edit sea una versión que tome como base la página actual para &#39;create-prompt&#39;, dejando en no editable lo que corresponda (eg. created_at).
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Previamente había ajustado un poco el formulario de create-prompt, para tener más espacio y una acción de Cancel. Para la edición, el modo Agente de Copilot en VS Code funciona perfectamente, y tras algunos ajustes visuales, queda una experiencia bastante completa.&lt;/p&gt;
&lt;p&gt;Para simplificar, elimino de momento del modelo el campo &#39;description&#39;, pues diría que un buen title es suficiente, y ya era opcional. Aplicaré el cambio manualmente también en la DB.&lt;/p&gt;
&lt;h3 id=&quot;prompts&quot; tabindex=&quot;-1&quot;&gt;Prompts&lt;/h3&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_11&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_11&quot;&gt; (3) Vista pública de un prompt&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En realidad, el orden lógico diría que es programar primero el listado de prompts públicos (página /prompts) y luego el detalle (página /prompts/id). Para que funcione lo primero, el deberemos agregar otra Policy en la DB, donde cualquier usuario pueda leer aquellos prompts que sean is_public&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE POLICY &amp;quot;Enable read access for all users on public prompts&amp;quot;
ON public.prompts
AS PERMISSIVE
FOR SELECT
TO PUBLIC
USING (
  is_public
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y así conseguimos avances, especialmente en el primer bloque: La home muestra ya un enlace al catalogo, se puede usar leyendo los últimos 20 elementos (de momento sin búsquedas, filtros por tag, etc.)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2025-05-coleccion-promptly/media/promptly_06_app_with_public_catalog.gif&quot; alt=&quot;promptly_06_app_with_public_catalog.gif&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;resumen&quot; tabindex=&quot;-1&quot;&gt;Resumen&lt;/h2&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_12&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_12&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_13&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_13&quot;&gt; Logarse después&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_14&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_14&quot;&gt; Persistir el prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_15&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_15&quot;&gt; Ejecutar un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_16&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_16&quot;&gt; Copiar un prompt al portapapeles&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_17&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_17&quot;&gt; Catálogo de prompts públicos&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_18&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_18&quot;&gt; Compartir un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_19&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_19&quot;&gt; Colecciones de prompts con tags&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_20&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_20&quot;&gt; Ajustes de prompt (idioma de salida, tono, etc.)&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-04-publicar-un-prompt/"/>
  </entry><entry>
    <title>Promptly 03 - Ejecutar un prompt</title>
    <updated>2026-11-06T11:06:25Z</updated>
    <id>https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-03-ejecutar-un-prompt/</id>
    <content type="html">&lt;div class=&quot;transclusion internal-embed is-loaded&quot;&gt;&lt;div class=&quot;markdown-embed&quot;&gt;
&lt;p&gt;&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-01-el-arranque-del-proyecto/&quot;&gt;Promptly 01 - El arranque del proyecto&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-02-listar-prompts/&quot;&gt;Promptly 02 - Listar prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-03-ejecutar-un-prompt/&quot;&gt;Promptly 03 - Ejecutar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-04-publicar-un-prompt/&quot;&gt;Promptly 04 - Publicar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-05-importacion-de-prompts/&quot;&gt;Promptly 05 - Importación de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-06-busqueda-de-prompts/&quot;&gt;Promptly 06 - Búsqueda de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-07-perfil-y-licencia/&quot;&gt;Promptly 07 - Perfil y licencia&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code class=&quot;language-table-of-contents&quot;&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;sesion-3&quot; tabindex=&quot;-1&quot;&gt;Sesión 3&lt;/h1&gt;
&lt;h2 id=&quot;punto-de-partida&quot; tabindex=&quot;-1&quot;&gt;Punto de partida&lt;/h2&gt;
&lt;h3 id=&quot;objetivos&quot; tabindex=&quot;-1&quot;&gt;Objetivos&lt;/h3&gt;
&lt;p&gt;Los mismos, principalmente de aprendizaje. Pero hoy decido investigar un poco más y pensar más en detalle en los usuarios del producto, antes de avanzar más en la construcción.&lt;/p&gt;
&lt;h4 id=&quot;que-existe-similar&quot; tabindex=&quot;-1&quot;&gt;¿Que existe similar?&lt;/h4&gt;
&lt;p&gt;Si esta iniciativa fuera un negocio, hubiera comenzado por aquí claro 😄... Viendo cosas similares en el mercado, parece que existen algunos &amp;quot;marketplace&amp;quot;, para la compra-venta de prompts (como &lt;strong&gt;Promptbase&lt;/strong&gt; o &lt;strong&gt;FlowGPT&lt;/strong&gt;...), y también un listado gratuito e interesante de prompts en inglés, llamado &lt;a href=&quot;https://github.com/f/awesome-chatgpt-prompts&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;awesome-prompts&lt;/a&gt;. Se ve interesante, pero contribuir prompts a golpe de commit no parece un flujo sencillo para usuarios no técnicos.&lt;/p&gt;
&lt;p&gt;Sobre este último repo y listado existe &lt;a href=&quot;http://prompts.chat/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://prompts.chat&lt;/a&gt;, una app gratuita que los lista y también permite ejecutarlos, pasándoselos como parámetro a varios LLM y abriendo pestañas a los modelos online (eg: &lt;a href=&quot;https://github.com/copilot&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://github.com/copilot&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Esto último, la forma de &lt;strong&gt;ejecutar rápidamente un prompt desde la app&lt;/strong&gt;, abriendo una ventana nueva sí me parece una &lt;em&gt;feature clave&lt;/em&gt;, así que la voy a desarrollar. Era evidente, pero no le había prestado interés inicialmente porque pensaba que me iba a requerir ejecución en línea, o lo que es lo mismo, tener api keys para los modelos (= añadir costes), pero como pasarela me encaja perfectamente.&lt;/p&gt;
&lt;h4 id=&quot;algunas-ideas-sobre-el-producto&quot; tabindex=&quot;-1&quot;&gt;Algunas ideas sobre el producto&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Compartir&lt;/strong&gt;&lt;br /&gt;
La gente crea prompts y los comparte ya... y esa tendencia irá a más. ¿Cómo lo hace ahora mismo? Pues con un modo manual, de hecho 100% manual, el creador le pasa el texto a alguien por mensaje, en red social, etc...&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(+) Ventajas
&lt;ul&gt;
&lt;li&gt;es sencillo y funciona&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;(-) Inconvenientes
&lt;ul&gt;
&lt;li&gt;En el &amp;quot;Modo manual&amp;quot; se pierde la &amp;quot;autoría original&amp;quot;&lt;/li&gt;
&lt;li&gt;No hay un lugar para el feedback&lt;/li&gt;
&lt;li&gt;la catalogación queda del lado del receptor (si quiere volver al prompt más adelante, y volverlo a ejecutar, tendrá que gestionarlos por su cuenta). En el mejor de los casos, el historial de la herramienta le permitirá recuperarlo, pero a día de hoy (por ejemplo en ChatGPT), esto es un lio, más si la persona (como yo) le gusta probar varios prompt, y va saltando de uno a otro (muchas veces porque agota los niveles free tier de cada plataforma).&lt;/li&gt;
&lt;li&gt;Si se usa el &#39;share chat&#39; nativo de la app, el prompt queda ligado a la herramienta. En general, y como norma para todo: &lt;em&gt;mantén el control de tu información&lt;/em&gt;, y que pueda circular por varias herramientas (en este caso, varios LLM, fácilmente), evita el vendor lock-in...&lt;/li&gt;
&lt;li&gt;En general, la experiencia puede ser mejor&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Colecciones y aprendizaje&lt;/strong&gt;&lt;br /&gt;
Algunos pensamientos en alto:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(a) &lt;em&gt;series de prompts:&lt;/em&gt; un prompt es muy útil, pero generalmente es solo el punto de partida, lo interesante suele ser una serie más elaborada, con preguntas enlazadas. ¿Tendría sentido relacionar unos prompts con otros linealmente en colecciones pensadas para ejecutarse en orden?&lt;/li&gt;
&lt;li&gt;(b) &lt;em&gt;aprendizaje&lt;/em&gt;: los prompts pueden servir para trabajo, para ocio... pero sin duda una de los usos principales que yo le veo es el aprendizaje (autónomo o guiado). ¿Tendría sentido hacer colecciones de prompts de aprendizajes sobre un tema?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;El modelo de datos podría hacerse más grande y contemplar cosas como &#39;colecciones ordenadas de prompts&#39;, por ejemplo. Pero de nuevo... &lt;em&gt;¿que es lo más sencillo que puede funcionar?&lt;/em&gt;. Pues diría que los &lt;strong&gt;tags&lt;/strong&gt;; ya hemos incorporado el campo al prompt en la DB y pueden servir como la formula más sencilla de agrupación de prompts, así que será el siguiente elemento en el que trabajaremos (después de la ejecución del prompt) . Además, si se quiere orden, pues puede ser tan fácil como usar un tag, por ejemplo &amp;quot;curso-programación&amp;quot; y luego usar nombres tal que &amp;quot;01 - Primer tema&amp;quot;, &amp;quot;02 - Segundo tema&amp;quot;, etc.&lt;/p&gt;
&lt;p&gt;Tras esta parte de brainstorming / investigación, las tareas y su orden quedarían en algo como este &lt;strong&gt;mini-roadmap&lt;/strong&gt;:&lt;/p&gt;
&lt;h3 id=&quot;tareas&quot; tabindex=&quot;-1&quot;&gt;Tareas&lt;/h3&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_0&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_0&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_1&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_1&quot;&gt; Logarse después&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_2&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_2&quot;&gt; Persistir y recuperar prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_3&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_3&quot;&gt; Ejecutar un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_4&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_4&quot;&gt; Copiar un prompt al portapapeles&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_5&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_5&quot;&gt; Compartir un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_6&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_6&quot;&gt; Catálogo de prompts públicos&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_7&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_7&quot;&gt; Colecciones de prompts con tags&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;acciones&quot; tabindex=&quot;-1&quot;&gt;Acciones&lt;/h2&gt;
&lt;h3 id=&quot;ejecutar-copiar-un-prompt&quot; tabindex=&quot;-1&quot;&gt;Ejecutar / copiar un prompt&lt;/h3&gt;
&lt;p&gt;Desde la app y fácilmente. Seguro se puede usar el API (pidiendo primero las credenciales al usuario y guardando su API-KEY en supabase, ofuscado), pero...&lt;em&gt;¿cuál es la opción más sencilla?&lt;/em&gt;  pues abriendo una nueva ventana externa con ChatGPT con el texto del prompt. Esto es trivial, usando la url que permite pasárselo como queryParam: &lt;a href=&quot;https://chatgpt.com/?prompt=elPrompt&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://chatgpt.com/?prompt=elPrompt&lt;/a&gt;. Más fácil imposible 😋.&lt;/p&gt;
&lt;p&gt;En algunos casos, al usuario le vendrá  mejor copiarlo para ejecutar de otra forma (ej. en una app local), así que programemos también un &lt;em&gt;Copiar al portapapeles&lt;/em&gt;, que es trivial.&lt;/p&gt;
&lt;h2 id=&quot;resumen&quot; tabindex=&quot;-1&quot;&gt;Resumen&lt;/h2&gt;
&lt;p&gt;En este punto, la app es funcional y se ve así:&lt;br /&gt;
&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2025-05-coleccion-promptly/media/promptly_04_run_in_chatgpt.gif&quot; alt=&quot;promptly_04_run_in_chatgpt.gif&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Sobre la lista de tareas, este es el estado al terminar la sesión:&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_8&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_8&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_9&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_9&quot;&gt; Logarse después&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_10&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_10&quot;&gt; Persistir y recuperar prompts&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_11&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_11&quot;&gt; Ejecutar un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_12&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_12&quot;&gt; Copiar un prompt al portapapeles&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_13&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_13&quot;&gt; Compartir un prompt&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_14&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_14&quot;&gt; Catálogo de prompts públicos&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_15&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_15&quot;&gt; Colecciones de prompts con tags&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-03-ejecutar-un-prompt/"/>
  </entry><entry>
    <title>Promptly 02 - Listar prompts</title>
    <updated>2026-11-06T11:06:25Z</updated>
    <id>https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-02-listar-prompts/</id>
    <content type="html">&lt;div class=&quot;transclusion internal-embed is-loaded&quot;&gt;&lt;div class=&quot;markdown-embed&quot;&gt;
&lt;p&gt;&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-01-el-arranque-del-proyecto/&quot;&gt;Promptly 01 - El arranque del proyecto&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-02-listar-prompts/&quot;&gt;Promptly 02 - Listar prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-03-ejecutar-un-prompt/&quot;&gt;Promptly 03 - Ejecutar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-04-publicar-un-prompt/&quot;&gt;Promptly 04 - Publicar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-05-importacion-de-prompts/&quot;&gt;Promptly 05 - Importación de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-06-busqueda-de-prompts/&quot;&gt;Promptly 06 - Búsqueda de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-07-perfil-y-licencia/&quot;&gt;Promptly 07 - Perfil y licencia&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code class=&quot;language-table-of-contents&quot;&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;sesion-2&quot; tabindex=&quot;-1&quot;&gt;Sesión 2&lt;/h1&gt;
&lt;h2 id=&quot;punto-de-partida&quot; tabindex=&quot;-1&quot;&gt;Punto de partida&lt;/h2&gt;
&lt;h3 id=&quot;objetivos&quot; tabindex=&quot;-1&quot;&gt;Objetivos&lt;/h3&gt;
&lt;p&gt;Se mantienen tal cual.&lt;/p&gt;
&lt;h3 id=&quot;tareas&quot; tabindex=&quot;-1&quot;&gt;Tareas&lt;/h3&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_0&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_0&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_1&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_1&quot;&gt; Logarse después (gestión de la identidad)&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_2&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_2&quot;&gt; Persistir y recuperar prompts&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Los 3 elementos previos apuntan a que la app debe persistir los datos y hacerlo de forma &lt;strong&gt;segura&lt;/strong&gt; (que los prompts sean de cada usuario y privados por defecto). Lo que es lo mismo, que un usuario liste y vea solo sus prompts y otro usuario los suyos. Esto se consigue de 2 formas tradicionalmente: una &lt;strong&gt;base de datos propia&lt;/strong&gt; (cuando el usuario está por ejemplo en un entorno dedicado, o bien la plataforma crea una para cada cuenta) o la misma base de datos / tablas pero un &lt;strong&gt;sistema robusto de RLS&lt;/strong&gt; (Row Level Security) que aisle los datos de cada cliente. Habitualmente lo 2º es más sencillo y eficiente en términos de costes, así que iremos por ese camino.&lt;/p&gt;
&lt;p&gt;🔝Es claro que los requisitos funcionales (gestionar los prompts), van acompañados necesariamente de requisitos de otro tipo. En un enfoque de mínimos, comenzaremos por una seguridad / usabilidad básica... pero más adelante, si la cosa crece, habría que revisar otros como el rendimiento del sistema o su mantenibilidad.&lt;/p&gt;
&lt;h2 id=&quot;acciones&quot; tabindex=&quot;-1&quot;&gt;Acciones&lt;/h2&gt;
&lt;h3 id=&quot;persistencia&quot; tabindex=&quot;-1&quot;&gt;Persistencia&lt;/h3&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_3&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_3&quot;&gt; Crear tabla de prompts&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Seguramente la estructura evolucionará, pero en una primera versión, y con ayuda de ChatGPT, esta es la estructura propuesta, con una sola tabla&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;CREATE TABLE prompts (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    description TEXT,
    tags TEXT[],
    is_public BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En &lt;strong&gt;Supabase&lt;/strong&gt; no hay una tabla &lt;code&gt;public.users&lt;/code&gt;, para hacer la FK. En la sección Authentication &amp;gt; Users muestra que existe sin embargo, con los campos UID, Display name, Email, Provider type, etc. Esta tabla se encuentra en realidad bajo &lt;em&gt;auth.users&lt;/em&gt;, así que omitiremos el FK explícito, pero habrá que tenerlo en cuenta para manejar la integridad de los datos (con algún tipo de borrado manual en cascada). Reemplazamos pues: &lt;code&gt;user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE&lt;/code&gt; con un simple &lt;code&gt;user_id UUID NOT NULL&lt;/code&gt;&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_4&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_4&quot;&gt; Privacidad de datos&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Usaremos RLS. Inicialmente Supabase (mediante un asistente con AI por cierto para los SQL muy útil), sugiere unas reglas que tienen bastante sentido, para limitar, via CREATE POLICY las operaciones a nivel de SELECT, INSERT, UPDATE, DELETE. Si más adelante incluimos el is_public, crearemos otra policy para ver los prompts compartidos&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- RLS basica
CREATE POLICY &amp;quot;Authenticated users can view their own prompts&amp;quot; 
ON public.prompts 
FOR SELECT 
TO authenticated 
USING ((select auth.uid()) = user_id);

CREATE POLICY &amp;quot;Authenticated users can insert prompts&amp;quot; 
ON public.prompts 
FOR INSERT 
TO authenticated 
WITH CHECK (true);

CREATE POLICY &amp;quot;Authenticated users can update their own prompts&amp;quot; 
ON public.prompts 
FOR UPDATE 
TO authenticated 
USING ((select auth.uid()) = user_id) 
WITH CHECK (true);

CREATE POLICY &amp;quot;Authenticated users can delete their own prompts&amp;quot; 
ON public.prompts 
FOR DELETE 
TO authenticated 
USING ((select auth.uid()) = user_id);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;🔝Supabase tiene una UI para definirlo, pero el SQL Editor es más flexible para crearlos.&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_5&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_5&quot;&gt; Realizar unas inserciones de prueba / datos seed y pruebas&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hay librerías custom para generar datos &#39;semilla&#39; de prueba, y muchas formas de hacerlo, pero lo más sencillo es pasarle el create table a un LLM cualquiera y pedirle N filas, con las restricciones que apliquen, por ejemplo:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-prompt&quot;&gt;sql
CREATE TABLE prompts (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    description TEXT,
    tags TEXT[],
    created_at TIMESTAMP WITH TIME ZONE DEFAULT now(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT now()
);


Generame 10 ejemplos de prompt para la tabla previa, para el usuario con id=&#39;18e3e69f-9723-462f-8e5f-edfca2e1232b&#39;, teniendo en cuenta que estoy utilizando Supabase, sobre un PostgreSQL 15.8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;🔝Por cierto, este sería un buen ejemplo de un prompt a almacenar en la plataforma, como un ejemplo de generador de mocks de postgres. ChatGPT genera un snippet compacto, con &lt;em&gt;generate_series&lt;/em&gt;,  algo como:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;-- Enable pgcrypto if not already enabled
CREATE EXTENSION IF NOT EXISTS pgcrypto;

-- Insert 10 random prompts
INSERT INTO prompts (user_id, title, content, description, tags)
SELECT
  &#39;el-id-del-usuario-que-toque&#39;::uuid,
  &#39;Prompt #&#39; || gs AS title,
  &#39;Escribe un texto creativo sobre &#39; || 
    (ARRAY[
      &#39;el futuro de la inteligencia artificial&#39;,
      &#39;una conversación entre dos árboles&#39;,
      &#39;una ciudad sumergida&#39;,
      &#39;el último pensamiento de un robot&#39;,
      &#39;un experimento fallido de viaje en el tiempo&#39;
    ])[floor(random() * 5 + 1)] || &#39;.&#39; AS content,
  &#39;Prompt auto-generado para hacer pruebas. Ref: &#39; || md5text AS description,
  ARRAY[
    (ARRAY[&#39;creatividad&#39;, &#39;tecnologia&#39;, &#39;ficcion&#39;, &#39;historia&#39;, &#39;misterio&#39;])[floor(random() * 5 + 1)],
    (ARRAY[&#39;filosofia&#39;, &#39;futuro&#39;, &#39;ia&#39;, &#39;naturaleza&#39;, &#39;distopia&#39;])[floor(random() * 5 + 1)]
  ] AS tags
FROM generate_series(1, 10) AS gs;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;🔝Y esto se ejecuta sin problema en Supabase. Bien Supabase, bien 💌&lt;/p&gt;
&lt;p&gt;Se puede tal vez agregar un tag extra, para borrarlos más adelante. Prompt en el propio Supabase, que tiene un interfaz en modo texto llamada &amp;quot;Assistant&amp;quot;: &lt;code&gt;Actualiza todo registro en &amp;quot;prompts&amp;quot; y agrega un tag &amp;quot;random_mock&amp;quot;, respetando las tags actuales&lt;/code&gt;... mmm parece que ahora mismo no funciona bien (dicen que está en Alpha), vamos a ChatGPT y genera un SQL que funciona bien:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;UPDATE prompts
SET tags = array_append(tags, &#39;random_mock&#39;)
WHERE NOT (&#39;random_mock&#39; = ANY(tags));
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;ui-basica-para-prompts&quot; tabindex=&quot;-1&quot;&gt;UI  básica para prompts&lt;/h3&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_6&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_6&quot;&gt; Listar prompts&lt;br /&gt;
Ya tenemos datos de prueba, con 10 prompts para un usuario, así que podemos empezar por listarlos y ver el detalle de 1 de ellos.&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Es muy sencillo con el cliente server-side para Supabase, las instrucciones en la template y un poco de ayuda de Copilot en VSCode para las clases de TailwindCSS, el resultado:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2025-05-coleccion-promptly/media/promptly_02_prompts_list.png&quot; alt=&quot;promptly_02_prompts_list.png&quot; /&gt;&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_7&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_7&quot;&gt; General: Modificar la UI para eliminar las referencias a la plantilla&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lo mínimo es eliminar algunos botones y referencias a la template. Aun quedará dead-code y cosas a limpiar en el futuro, pero sirve para avanzar. Vamos a usar como &amp;quot;nombre del proyecto&amp;quot;: Promptly&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_8&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_8&quot;&gt; Formulario para registrar el prompt&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Se necesita un usuario logado, y otra página, por lo que es un buen momento para considerar por primer vez las &amp;quot;rutas&amp;quot; de forma más general, y los caminos que seguirán los usuarios.&lt;/p&gt;
&lt;h3 id=&quot;rutas&quot; tabindex=&quot;-1&quot;&gt;Rutas&lt;/h3&gt;
&lt;p&gt;Hasta ahora tenemos estas rutas (cada una correspondiendo con un page.tsx de NextJS):&lt;br /&gt;
&lt;strong&gt;Actual&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;/&lt;/strong&gt;: la &amp;quot;home&amp;quot;, de momento con el contenido de la plantilla y solo para usuarios no logados. Seguramente modificaremos la lógica, pues idealmente los usuarios podrán obtener valor de la app, aun sin registrarse (pero también tras hacerlo: ejemplo últimos prompts subidos, búsqueda por tags, votos, etc.).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;/sign-up&lt;/strong&gt;: para crear la cuenta. Inicialmente activo solo el modo usuario &amp;amp; clave. Más adelante consideraremos social-login con Auth.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;/sign-in&lt;/strong&gt;: para logarse&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;/forgot-password&lt;/strong&gt;: para recuperar el pwd via email&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;/protected&lt;/strong&gt;:  información privada del usuario. En este punto solo el listado de prompts del usuario&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;/protected/reset-password&lt;/strong&gt;: para rellenar ese flujo relacionado con la clave.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Y considerando lo que queremos hacer, esta podría ser una buena estructura. Por simplicidad, dejemos fuera cuestiones en el diseño de las urls como paginación, filtros, etc. (que veremos más adelante).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;(a) Rutas públicas&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ruta&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;/&lt;/td&gt;
&lt;td&gt;Home pública&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/prompts&lt;/td&gt;
&lt;td&gt;Catálogo público de prompts. Navegable, con filtros por tags, nombre, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/prompts/[id]&lt;/td&gt;
&lt;td&gt;Detalle de un prompt específico. Accesible sin login.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/sign-up&lt;/td&gt;
&lt;td&gt;Registro de usuario.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/sign-in&lt;/td&gt;
&lt;td&gt;Login de usuario.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;(b) Rutas privadas&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ruta&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;/dashboard&lt;/td&gt;
&lt;td&gt;Página principal del usuario logado. Puede listar sus prompts, perfil, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/dashboard/prompts&lt;/td&gt;
&lt;td&gt;Listado de prompts del usuario logado (tipo /protected actual).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/dashboard/prompts/new&lt;/td&gt;
&lt;td&gt;Crear nuevo prompt.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;home-con-logo&quot; tabindex=&quot;-1&quot;&gt;Home con logo&lt;/h3&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_9&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_9&quot;&gt; Limpieza básica de Home y su componente &amp;quot;Hero&amp;quot; (+ permitir que no fuerce la redirección a dashboard)&lt;br /&gt;
Cambiamos el texto y creamos un logo con ayuda de ChatGPT&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-prompt&quot;&gt;Hazme un logo sencillo, en SVG que diga &amp;quot;Promptly&amp;quot;, con la P más destacada, tomando como referencia algo similar a este de NextJS:

--y-le-pego-el-SVG-tal-cual-de-NextJS (omitido)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y nos genera algo para empezar...&lt;br /&gt;
&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2025-05-coleccion-promptly/media/promptly_03_home_with_hero.png&quot; alt=&quot;promptly_03_home_with_hero.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Vale sí, es bastante feo, pero sirve para empezar. Además, como es un SVG con el fill=&amp;quot;currentColor&amp;quot;, se adapta también a la feature de theme de la template (tema oscuro), por lo que de momento👌.&lt;/p&gt;
&lt;h3 id=&quot;crear-y-listar-prompts&quot; tabindex=&quot;-1&quot;&gt;Crear y listar prompts&lt;/h3&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_10&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_10&quot;&gt; Nueva estructura para la zona privada (componentes y esbozo de interfaz)&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Las rutas son algo importante y en lo que es fácil cometer errores. En la plantilla actualmente las rutas están hardcodeadas, y repetidas en varios lugares. Vamos a crear un fichero &amp;quot;&lt;em&gt;routes.ts&lt;/em&gt;&amp;quot; para tenerlas controladas y poder cambiarlas después sin romper.&lt;/p&gt;
&lt;p&gt;Tras varios refactor, con algo de ayuda de ChatGPT / Copilot se puede avanzar y tener una app funcional para el flujo básico: guardar y listar prompts de un usuario en modo privado.&lt;/p&gt;
&lt;h2 id=&quot;resumen&quot; tabindex=&quot;-1&quot;&gt;Resumen&lt;/h2&gt;
&lt;p&gt;En este punto, ya tenemos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Una home básica, con una hero section + logo, adaptada al proyecto, bautizado con Promptly&lt;/li&gt;
&lt;li&gt;Una estructura básica de UI para las distintas páginas&lt;/li&gt;
&lt;li&gt;La capacidad de hacer los flujos básicos de un usuario: sign-in / sign-up / forgot-password, con tipo usuario-password&lt;/li&gt;
&lt;li&gt;La feature de crear un Prompt con sus campos básicos y que se persista&lt;/li&gt;
&lt;li&gt;La feature de listar los Prompt de un usuario, gracias a Row Level Security.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Si revisamos la lista de tareas tenemos:&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_11&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_11&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_12&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_12&quot;&gt; Logarse después (gestión de la identidad)&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_13&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_13&quot;&gt; Persistir y recuperar prompts&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-02-listar-prompts/"/>
  </entry><entry>
    <title>Promptly 01 - El arranque del proyecto</title>
    <updated>2026-11-06T11:06:25Z</updated>
    <id>https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-01-el-arranque-del-proyecto/</id>
    <content type="html">&lt;p&gt;Con este post inicio una serie sobre la construcción de un side-project que he llamado Promptly, para la gestión de prompts de IA. Iré enlazando aquí los posts conforme los vaya publicando, para que sea más fácil navegar entre ellos.&lt;/p&gt;
&lt;p&gt;Si simplemente prefieres probarlo, ve a &lt;a href=&quot;https://promptly.es/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://promptly.es&lt;/a&gt;. Si tienes curiosidad cómo lo he creado, sigue leyendo.&lt;/p&gt;
&lt;div class=&quot;transclusion internal-embed is-loaded&quot;&gt;&lt;div class=&quot;markdown-embed&quot;&gt;
&lt;p&gt;&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-01-el-arranque-del-proyecto/&quot;&gt;Promptly 01 - El arranque del proyecto&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-02-listar-prompts/&quot;&gt;Promptly 02 - Listar prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-03-ejecutar-un-prompt/&quot;&gt;Promptly 03 - Ejecutar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-04-publicar-un-prompt/&quot;&gt;Promptly 04 - Publicar un prompt&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-05-importacion-de-prompts/&quot;&gt;Promptly 05 - Importación de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-06-busqueda-de-prompts/&quot;&gt;Promptly 06 - Búsqueda de prompts&lt;/a&gt;&lt;br /&gt;
&lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-07-perfil-y-licencia/&quot;&gt;Promptly 07 - Perfil y licencia&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code class=&quot;language-table-of-contents&quot;&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;objetivos&quot; tabindex=&quot;-1&quot;&gt;Objetivos&lt;/h1&gt;
&lt;h2 id=&quot;problema-oportunidad&quot; tabindex=&quot;-1&quot;&gt;Problema / Oportunidad&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Realizar buenas preguntas a los modelos de IA (generalmente a través de interfaces como ChatGPT, Claude, etc.) es a veces complejo.&lt;/li&gt;
&lt;li&gt;Los LLMs que los soportan son muy potentes, pero también muy abiertos y aún existe poca experiencia colectiva sobre cómo usarlos.&lt;/li&gt;
&lt;li&gt;Partir de una pregunta / prompt puede ayudar mucho y servir de inspiración para explorar / profundizar siguiendo ese modelo.&lt;/li&gt;
&lt;li&gt;La gente comparte, cada vez más, sus prompts (por ejemplo en Twitter, LinkedIn, etc.)&lt;/li&gt;
&lt;li&gt;Existen varias alternativas online para almacenar prompts, pero la mayoría siguen un modelo de marketplace (y además son en Inglés, lo que supone una barrera para algunas personas).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;reto&quot; tabindex=&quot;-1&quot;&gt;Reto&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Crear un lugar donde gestionar los prompts y poder compartir, descubrir y probar de forma sencilla otros nuevos puede ser muy útil.&lt;/li&gt;
&lt;li&gt;Construir algo así, pero también describir el proceso y las decisiones, me parece una experiencia interesante profesionalmente, así que voy a hacerlo a través de varios post sucesivos, describiendo lo realizado, sesión a sesión (de forma orientativa, una sesión puede ser un bloque aproximado de tiempo de 1 a 2 horas aprox).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;proceso&quot; tabindex=&quot;-1&quot;&gt;Proceso&lt;/h2&gt;
&lt;p&gt;En cada post iré recogiendo el proceso de construcción y seguiré un esquema parecido:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;Punto de partida: los requisitos en ese punto (que irán cambiando conforme avancemos)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Acciones: cosas concretas hechas esa sesión para avanzar en la construcción del proyecto&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Balance: un balance de la sesión, revisando los resultados&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sin más dilación, vamos a ello, con la primera sesión de trabajo.&lt;/p&gt;
&lt;h1 id=&quot;sesion-1&quot; tabindex=&quot;-1&quot;&gt;Sesión 1&lt;/h1&gt;
&lt;h2 id=&quot;punto-de-partida&quot; tabindex=&quot;-1&quot;&gt;Punto de partida&lt;/h2&gt;
&lt;h3 id=&quot;objetivos-1&quot; tabindex=&quot;-1&quot;&gt;Objetivos&lt;/h3&gt;
&lt;p&gt;A alto nivel estos serían los &lt;strong&gt;objetivos actuales del proyecto&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Quiero permitir guardar prompts para poder gestionarlos / usarlos mejor.&lt;/li&gt;
&lt;li&gt;Quiero a la vez hacer involucrarme en un proyecto que me permita aprender más sobre algunas tecnologías (NextJS, TailwindCSS y otras)&lt;/li&gt;
&lt;li&gt;Quiero usar lo que sea más sencillo y rápido / barato.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;tareas&quot; tabindex=&quot;-1&quot;&gt;Tareas&lt;/h3&gt;
&lt;p&gt;Vamos a sacar una lista primera de &lt;strong&gt;tareas&lt;/strong&gt; (o requisitos si se quiere), para construir el proyecto. Lo que quiero es dejar al usuario guardar sus prompts, pero para conseguir esto, se necesita un mínimo de elementos previos (infraestructura):&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_0&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_0&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_1&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_1&quot;&gt; Logarse después&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_2&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_2&quot;&gt; Persistir un prompt&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Y todo esto debe ser seguro, claro: que los prompts sean de cada usuario y privados por defecto.&lt;/p&gt;
&lt;p&gt;Venga, pues con este punto de partida, vamos a ponernos manos a la obra.&lt;/p&gt;
&lt;h2 id=&quot;acciones&quot; tabindex=&quot;-1&quot;&gt;Acciones&lt;/h2&gt;
&lt;h3 id=&quot;eleccion-de-plataforma&quot; tabindex=&quot;-1&quot;&gt;Elección de plataforma&lt;/h3&gt;
&lt;h4 id=&quot;firebase&quot; tabindex=&quot;-1&quot;&gt;Firebase&lt;/h4&gt;
&lt;p&gt;Una forma que conozco y seguramente funcionaría bien es &lt;em&gt;Firebase&lt;/em&gt;; ya lo he usado para algunos proyectos en el pasado, incluyendo la gestión de la autenticación y la persistencia de JSON (su motor Firebase es NoSQL) y los updates en tiempo real, y funciona muy bien, sin duda.  Seguramente podría probar el nuevo &lt;a href=&quot;https://cloud.google.com/blog/products/application-development/firebase-studio-lets-you-build-full-stack-ai-apps-with-gemini&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Firebase Studio&lt;/a&gt;... pero me gustaría más usar un motor SQL estándar... así que voy a ver otras opciones en GCP.&lt;/p&gt;
&lt;h4 id=&quot;postgre-sql-en-gcp&quot; tabindex=&quot;-1&quot;&gt;PostgreSQL en GCP&lt;/h4&gt;
&lt;p&gt;He trabajado con muchos tipos de base de datos: SQL Server, Oracle,  Postgres, ClickHouse... pero como lei en alguna ocasión: &amp;quot;&lt;em&gt;en caso de duda, usa postgres&lt;/em&gt;&amp;quot;. Así que he echado un ojo a mi cuenta Google Cloud Platform (GCP), y veo que actualmente una instancia gestionada de Postgres 16, con 2 vCPU y 8 GB + 10 GB SSD cuesta (en Iowa) $0.14/hora, lo que vendría a ser aprox $102 dólares al mes (en Europa GCP es más caro, y sobre un coste unitario de $0.17 hora, se iría hasta casi $125 mes, pero para un prototipo / side-project la latencia no me preocupa demasiado).&lt;/p&gt;
&lt;p&gt;A pesar de que tengo un descuento disponible en GCP,  la verdad, 0 € para arrancar me sonaría mejor, y me apetece probar algo menos enterprise que GCP.&lt;/p&gt;
&lt;h4 id=&quot;supabase&quot; tabindex=&quot;-1&quot;&gt;Supabase&lt;/h4&gt;
&lt;p&gt;Y así he llegado hasta Supabase, que se vende como una alternativa open-source a Firebase e incluye Postgres y un plan inicial de $0 / mes. así que le voy a dar una oportunidad.&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_3&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_3&quot;&gt; Creación de proyecto &amp;quot;promptly&amp;quot; en region West Europe&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;[!info]&lt;br /&gt;
El plan en Supabase tampoco es para tirar cohetes, veremos después como se comporta. Se trata del plan Nano = $0 / hour (up to 0.5 GB de memoria y una CPU Shared) + Disk size: 1 GB&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;creacion-de-la-app&quot; tabindex=&quot;-1&quot;&gt;Creación de la app&lt;/h3&gt;
&lt;p&gt;Hoy en día existen muchas fórmulas, pero me apetece profundizar más en NextJS. Un proyecto NextJS &amp;quot;normal&amp;quot;, puede ser creado en local fácilmente con&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;npx create-next-app@latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pero también se admiten &#39;templates&#39; o plantillas pre-configuradas. Este es el caso de Supabase, ya que existe una &lt;strong&gt;plantilla oficial&lt;/strong&gt; que facilita el arranque:&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_4&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_4&quot;&gt; Creación del proyecto NextJS con la plantilla oficial de supabase&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;npx create-next-app@latest -e with-supabase
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;la-plantilla&quot; tabindex=&quot;-1&quot;&gt;La plantilla&lt;/h4&gt;
&lt;p&gt;Parece que la template tiene buena pinta, con instrucciones claras en la página principal para tener el proyecto bien conectado al entorno e incluso un formulario de sign-up integrado + Sign in.&lt;/p&gt;
&lt;p&gt;El proyecto incluye:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Una home que se loga y verifica que tiene acceso al proyecto&lt;/li&gt;
&lt;li&gt;Una cabecera con el típico Sig In / Sign out&lt;/li&gt;
&lt;li&gt;Un formulario para Sign up&lt;/li&gt;
&lt;li&gt;Una pagina privada (/protected)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Técnicamente, la plantilla incluye este stack:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dependencies:
&lt;ul&gt;
&lt;li&gt;next: latest (hoy 15.3.1)&lt;/li&gt;
&lt;li&gt;react: 19.0.0&lt;/li&gt;
&lt;li&gt;@supabase/ssr: latest (hoy 0.6.1)&lt;/li&gt;
&lt;li&gt;@supabase/supabase-js: latest (hoy 2.49.4)&lt;/li&gt;
&lt;li&gt;@radix-ui/react-&lt;code&gt;&#92;*&lt;/code&gt;: varios paquetes paquetes de UI. &lt;a href=&quot;https://www.radix-ui.com/primitives/docs/overview/introduction&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Ver aquí&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;dev
&lt;ul&gt;
&lt;li&gt;typescript: 5.7.2&lt;/li&gt;
&lt;li&gt;tailwindcss: 3.4.17&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Y existe un .env que puede fácilmente usarse de plantilla para conectar al proyecto supabase real desde local (&lt;code&gt;env.local&lt;/code&gt;), simplemente rellenando estas variables:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;NEXT_PUBLIC_SUPABASE_URL=https://tu-url-de-proyecto-en.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=tu-clave-para-conectar-ver-dashboard-en-supabase
&lt;/code&gt;&lt;/pre&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_5&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_5&quot;&gt; Configuración del proyecto NextJS contra Supabase y creación del primer usuario&lt;br /&gt;
Para probar la plantilla y la parte privada, procedo a crearme un usuario. La aplicación se puede ver en local y jugar con ella fácilmente, como en cualquier otro proyecto NextJS&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;primer-despliegue&quot; tabindex=&quot;-1&quot;&gt;Primer despliegue&lt;/h3&gt;
&lt;p&gt;Ok, arrancamos con la plantilla tal cual, sin modificaciones, pero siempre gusta ver el producto en funcionamiento en un entorno real. Se podrían usar varios entornos, pero el más natural para NextJS sigue siendo Vercel, tal y como aconseja la plantilla. Como ya tengo una cuenta gratuita de Vercel, procedo a enlazar mi proyecto git local con estos pasos:&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_6&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_6&quot;&gt; Subir proyecto git a Github (enlazar y subir el repo local, inicializado por la template)&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_7&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_7&quot;&gt; Configurar el proyecto en Vercel, importando ese proyecto de github (en este caso, llamo al proyecto de Vercel de la misma forma que el repo: &amp;quot;promptly&amp;quot;).&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_8&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_8&quot;&gt; Para que todo funcione ok en otros entornos (más allá de localhost:3000) es preciso configurar las redirecciones de URLs adecuadas a los dominos usados.&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La propia template detecta y propone las URLs, lo cual es un muy buen detalle de devX por parte de Supabase:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;This particular deployment is&amp;quot;production&amp;quot; onhttps://promptly-m15aqmuuq-velardev-projects.vercel.app.

You will need to [update your Supabase project](https://supabase.com/dashboard/project/_/auth/url-configuration) with redirect URLs based on your Vercel deployment URLs.

- http://localhost:3000/**
- https://promptly-gilt.vercel.app/**
- https://promptly-gilt-*-[vercel-team-url].vercel.app/** (Vercel Team URL can be found in [Vercel Team settings](https://vercel.com/docs/accounts/create-a-team#find-your-team-id))
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;resumen&quot; tabindex=&quot;-1&quot;&gt;Resumen&lt;/h2&gt;
&lt;p&gt;En este punto, tenemos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Un proyecto &amp;quot;promptly&amp;quot; en Supabase, inicialmente vacío&lt;/li&gt;
&lt;li&gt;Un repo privado &amp;quot;promptly&amp;quot; en Github, con el proyecto NextJs de plantilla de supabase&lt;/li&gt;
&lt;li&gt;Un proyecto &amp;quot;promptly&amp;quot; en Vercel, vinculado a Github, para el hosting de la app.&lt;/li&gt;
&lt;li&gt;El proyecto desplegado por primera vez en una url pública&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2025-05-coleccion-promptly/media/promptly_01_template_deployed_to_vercel.png&quot; alt=&quot;promptly_01_template_deployed_to_vercel.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Sobre la lista inicial de tareas, terminamos la sesión con algo como:&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_9&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_9&quot;&gt; Registrarse para crear una cuenta&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_10&quot; checked=&quot;true&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_10&quot;&gt; Logarse después&lt;/label&gt;&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; id=&quot;cbx_11&quot; disabled=&quot;true&quot; /&gt;&lt;label for=&quot;cbx_11&quot;&gt; Persistir y recuperar prompts&lt;/label&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2025-05-coleccion-promptly/promptly-01-el-arranque-del-proyecto/"/>
  </entry><entry>
    <title>Con la IA tu trabajo es aprender</title>
    <updated>2026-10-14T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2026-10-14-tu-trabajo-es-aprender/con-la-ia-tu-trabajo-es-aprender/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2026-10-14-tu-trabajo-es-aprender/media/portada-tu-trabajo-es-aprender.jpg&quot; alt=&quot;portada-tu-trabajo-es-aprender.jpgIlustración de portada: un recorrido de líneas finas rosa y marfil que conecta un mapa, código, una lista de revisión, servidores y un libro, con una persona y un pequeño asistente de IA&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Con la IA tu trabajo desarrollando software es &lt;strong&gt;más que nunca aprender&lt;/strong&gt;. Aprender lo que necesita tu usuario y aprender de tu propio sistema.&lt;/p&gt;
&lt;p&gt;Tradicionalmente, mientras escribías los ficheros fuente o resolvías un bug complicado, construías en tu cabeza un conjunto de modelos mentales. El resultado era siempre doble: uno tangible (los fuentes y sobre todo el sistema en ejecución) y otro más intangible, interno, en forma de aprendizaje y heurísticos, del que seguramente no te dabas cuenta.&lt;/p&gt;
&lt;p&gt;Ahora el proceso de escritura del código (y de resolver bugs, y de cada vez más cosas) lo delegas en la IA, un cambio que ha sucedido muy rápidamente (recuerdo ser un poco escéptico al principio, cuando leía que Boris Cherny de Anthropic desinstaló su IDE, pero desde luego no lo soy, en 2026 es realidad al 100%). Y un problema derivado es que &lt;strong&gt;esos modelos mentales dejan de formarse naturalmente en tu cabeza&lt;/strong&gt;. O ya los tenías o te toca currártelo. Esto es importante para todos, pero especialmente para desarrolladores más junior, que no han tenido experiencia durante la era pre-IA.&lt;/p&gt;
&lt;p&gt;Vamos a partir de la premisa que este aprendizaje será siempre gradual, y también que quieres ser lo más productivo lo antes posible. Así que hablemos de los pasos mínimos para poner una feature en producción y algunos consejos en cada paso.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2026-10-14-tu-trabajo-es-aprender/media/ciclo-aprender.webp&quot; alt=&quot;ciclo-aprender.webpLos cuatro pasos para llevar una feature a producción con IA: aprender a navegar, crear la feature con IA, revisarla en equipo y llevarla a producción. Lo que ves en producción empieza la siguiente vuelta&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;aprender-a-navegar&quot; tabindex=&quot;-1&quot;&gt;Aprender a navegar&lt;/h2&gt;
&lt;h3 id=&quot;create-un-esquema-visual-del-sistema&quot; tabindex=&quot;-1&quot;&gt;Créate un esquema visual del sistema&lt;/h3&gt;
&lt;p&gt;Si empiezas con un sistema nuevo (o en una nueva empresa) y el sistema no es trivial (spoiler, casi nunca lo será), quieres tener un mapa mental para poder navegarlo. Pídeselo a tu LLM de referencia y que produzca un resultado visual, pero luego no lo dejes ahí, revísalo un buen rato (tómate un café y analízalo bien): pregunta sobre la arquitectura, los componentes más complejos, los puntos que no te convenza su diseño...&lt;/p&gt;
&lt;p&gt;Debes tener claras al menos 2 vistas: la vista lógica del sistema + la vista real de los componentes que se despliegan en tu cloud.&lt;/p&gt;
&lt;p&gt;Que el trabajo de exploración sea iterativo: ve realizando preguntas al LLM y mejorando poco a poco los diagramas / notas que tomes. &lt;em&gt;Si no tienes nada claro qué preguntar… háztelo mirar :).&lt;/em&gt; Y si tienes algún sistema organizado para tomar tus notas, en algo como Obsidian / un cerebro digital, +100 puntos.&lt;/p&gt;
&lt;p&gt;Lógicamente, si alguien ha escrito algo previo al respecto o puede darte un paseo en una reunión, pues te va a venir muy bien, aprovéchalo. Pero idealmente haz el trabajo antes y ve ya con preguntas a esa persona: ¿por qué se ha hecho esto así? ¿Cuáles son las partes más complejas, que funciona mal y queremos cambiar pronto?&lt;/p&gt;
&lt;p&gt;Esto aplica a menor escala también, claro, lo puedes aplicar con distinto nivel de zoom a piezas más pequeñas como un subsistema.&lt;/p&gt;
&lt;h3 id=&quot;aprende-del-producto&quot; tabindex=&quot;-1&quot;&gt;Aprende del Producto&lt;/h3&gt;
&lt;p&gt;La feature que te toque: entiéndela. Para qué sirve y por qué es importante para el usuario. &lt;em&gt;No, de verdad, ¡entiéndela!&lt;/em&gt;. Muchas veces los desarrolladores han programado soluciones sin entender realmente lo que hacían. Y si ya la entiendes, intenta crecer ese conocimiento en temas relacionados (¿qué viene después?¿cómo encaja esto en el flujo general?¿se puede mejorar algo?)&lt;/p&gt;
&lt;p&gt;Conforme avances en tu conocimiento, crecerá la calidad de lo que haces y tu impacto.&lt;/p&gt;
&lt;h2 id=&quot;crear-la-feature-con-ia&quot; tabindex=&quot;-1&quot;&gt;Crear la feature con IA&lt;/h2&gt;
&lt;h3 id=&quot;no-tengas-skill-itis&quot; tabindex=&quot;-1&quot;&gt;No tengas skill-itis&lt;/h3&gt;
&lt;p&gt;Tener una subscripción a Claude o ChatGPT debería bastar. Hay un montón de ruido en línea sobre mejoras, skills, prompts… pero mi consejo es que no tengas “skillitis”, y te vuelvas loco con mil historias (que lo más probable es que contaminen el contexto si no están claras y ordenadas). La tendencia parece ser que conforme los modelos crecen más en potencia, las skills son menos necesarias, pero al menos siempre habrá un mínimo sobre “cómo os gusta hacer las cosas en tu equipo”, osea que algo podrás afinar.&lt;/p&gt;
&lt;h3 id=&quot;ten-un-proceso-basico-de-desarrollo&quot; tabindex=&quot;-1&quot;&gt;Ten un proceso básico de desarrollo&lt;/h3&gt;
&lt;p&gt;En mi experiencia, la clave no son las skills sino tener claro el proceso de desarrollo que quieres seguir y cuáles son los puntos clave donde encajas tú como humano vs lo que hace la IA. Esto puede cambiar en función de tu equipo, experiencia, hábitos… no hay una bala de plata, pero es bueno que al menos haya un consenso básico en el equipo.&lt;/p&gt;
&lt;p&gt;Dicho esto, y si aun no tienes un proceso claro, te animo a probar cosas como las de &lt;a href=&quot;https://skills.addy.ie/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Addy Osmani Agent Skills&lt;/a&gt;, un tipo listo que ahora además se ha ido a trabajar de Google a Antrophic, algo sabrá.&lt;/p&gt;
&lt;p&gt;Los dos puntos más importantes: &lt;strong&gt;que la especificación inicial esté clara&lt;/strong&gt; y &lt;strong&gt;que esté claro cómo vas a probar&lt;/strong&gt; que lo desarrollado lo cumple.&lt;/p&gt;
&lt;h3 id=&quot;estilo-de-desarrollo-flexible&quot; tabindex=&quot;-1&quot;&gt;Estilo de desarrollo flexible&lt;/h3&gt;
&lt;p&gt;Aprende a leer el contexto de tu empresa para saber el estilo de desarrollo que te toca en cada caso. A veces toca ir lo más rápido posible, cuando se trata de validar una idea. No tiene sentido invertir en alta calidad (aún) si es un sólo un prototipo para 1 o 2 clientes, bajo feature flag. La inversa aplica también, si por ejemplo trabajas en un proyecto con una pasarela de pagos para cobros automatizados, invierte en calidad o te morderá en producción, y no querrás estar ahí.&lt;/p&gt;
&lt;h2 id=&quot;revisar-la-feature-en-equipo&quot; tabindex=&quot;-1&quot;&gt;Revisar la feature en equipo&lt;/h2&gt;
&lt;h3 id=&quot;hazte-cr&quot; tabindex=&quot;-1&quot;&gt;Hazte CR&lt;/h3&gt;
&lt;p&gt;Con LLM, a mano, como quieras, pero por respeto: no le lances algo a un compañero un PR sin haberlo mirado tú antes con ojos críticos. Antes los PRs los ‘esculpías’ paso a paso, tenías 100% claro lo que incluían, cada línea, cada IF... Ahora si vas deprisa y no te das cuenta, lo mismo metes en el PR un tema secundario que toca 10 ficheros (porque lo discutiste con el agente y no quedó claro si iba en scope).&lt;/p&gt;
&lt;p&gt;Si tenéis alguna acción de revisión automática con IA en el CI, mírala antes de que te diga lo mismo un compañero, no?&lt;/p&gt;
&lt;h3 id=&quot;cuida-la-descripcion-de-tus-pr&quot; tabindex=&quot;-1&quot;&gt;Cuida la descripción de tus PR&lt;/h3&gt;
&lt;p&gt;Ya sabemos que los modelos abren y cierran PRs como locos, pero también que luego a veces es imposible entender eso de forma clara desde el otro lado. La comunicación clara es un punto esencial.&lt;/p&gt;
&lt;p&gt;Yo trabajo bastante con interfaces así que +100 a usar imágenes o un pequeño video explicativo de los cambios, siempre ayuda mucho. Si son APIs, pues mete unos curl de prueba / descripción de payload: todo lo que facilite el trabajo al revisor. Ojo, que puedes pedirle pantallazos, videos, planes de pruebas… a tu LLM. No digo que hagas todo a mano, pero sí que te leas / entiendas / modifiques a tu criterio todo antes de que llegue a una persona.&lt;/p&gt;
&lt;p&gt;Del otro lado, como revisor, siempre espero algo como una explicación clara en 2 líneas de lo que hace el PR y un soporte gráfico de lo que hace (pantallas, diagrama de arquitectura…) / listado áreas que toca. Si el creador no te lo da, pues ya sabes, se lo puedes pedir al LLM cuando empieces lo primero.&lt;/p&gt;
&lt;h3 id=&quot;prueba-bien-la-feature&quot; tabindex=&quot;-1&quot;&gt;Prueba bien la feature&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Ya, ya, que pruebas a cada paso, en tu local, que tienes tests unitarios, de integración, de e2e…&lt;/em&gt; pero hombre prueba bien lo que has hecho al final (generalmente en el entorno dedicado asociado al PR).&lt;/p&gt;
&lt;p&gt;Bonus, si te grabaste un mini-video (tipo Loom) y/o si le pediste a tu LLM unas pruebas de aceptación contra el dedicado.&lt;/p&gt;
&lt;h2 id=&quot;llevar-la-feature-a-produccion&quot; tabindex=&quot;-1&quot;&gt;Llevar la feature a producción&lt;/h2&gt;
&lt;h3 id=&quot;decide-para-que-clientes-entornos-se-activa&quot; tabindex=&quot;-1&quot;&gt;Decide para qué clientes / entornos se activa&lt;/h3&gt;
&lt;p&gt;Aquí los feature flags y las estrategias de despliegue son la pieza clave. Si no has trabajado con estos sistemas, &lt;em&gt;pruébalos, ya!&lt;/em&gt;, merecen cada hora invertida.&lt;/p&gt;
&lt;h3 id=&quot;vigila-las-migraciones&quot; tabindex=&quot;-1&quot;&gt;Vigila las migraciones&lt;/h3&gt;
&lt;p&gt;Una buena gestión del modelo de datos, y en especial de las migraciones, es una de las claves de cualquier puesta en producción. Revísalas, pruébalas a fondo. Durante la CR dedica especial tiempo a ese bloque.&lt;/p&gt;
&lt;p&gt;La IA podrá ayudarte más adelante a cambiar el código, pero no inventarse datos críticos de negocio que no generaste o perdiste por un bug.&lt;/p&gt;
&lt;h3 id=&quot;piensa-la-observabilidad-desde-el-principio&quot; tabindex=&quot;-1&quot;&gt;Piensa la observabilidad desde el principio&lt;/h3&gt;
&lt;p&gt;La observabilidad es una cualidad clave del sistema, no algo que se añade al final. Diseña desde el principio cómo se va a ver la feature en producción y cómo la vas a poder observar (tú o un agente, claro).&lt;/p&gt;
&lt;p&gt;Eso implica desarrollar teniendo especialmente en cuenta las trazas. Algunas cosas que puedes hacer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Antes de escribir código, pregúntate (o pregúntale a tu LLM): ¿cómo voy a saber en producción que esta feature funciona y que la usan?. Apunta 2 o 3 señales: un evento al completar el flujo, un contador de errores, el tiempo de la operación nueva...&lt;/li&gt;
&lt;li&gt;Mételo en la especificación: pide trazas en los puntos clave, con el identificador del cliente, para que nazcan con el código y no se añadan después.&lt;/li&gt;
&lt;li&gt;Antes de mergear, con las métricas ya activas en desarrollo o integración, pídele a tu herramienta (por ejemplo Honeycomb) el dashboard de la feature y comprueba que cuenta lo que esperabas.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;La IA es una gran revolución, sin duda, y las técnicas van a seguir cambiando: lo que hoy es una skill mañana será una capacidad más del modelo. Pero muchos principios se mantienen, y a mí me parecen especialmente importantes tres:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Comunicación:&lt;/strong&gt; que cualquiera (tu compañero, el revisor, tú mismo dentro de seis meses, el agente…) entienda qué hace el sistema y por qué.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vigilancia de los pasos clave:&lt;/strong&gt; delegar la escritura en la IA no es delegar la responsabilidad. Hay puntos donde sigues tú: la especificación, tu propia CR, la prueba final y sobre todo lo que pasa en producción.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Valor al cliente:&lt;/strong&gt; el código siempre es un medio. Lo que cuenta es que lo que has construido le sirva a alguien.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Y por debajo de los tres, la premisa de todo el post: &lt;strong&gt;tu trabajo es aprender&lt;/strong&gt;. La IA puede escribir el código, pero no puede aprender por ti. Úsala para aprender más y mejor, no para dejar de hacerlo.&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2026-10-14-tu-trabajo-es-aprender/con-la-ia-tu-trabajo-es-aprender/"/>
  </entry><entry>
    <title>Con la IA los programadores ya vivimos en SimCity</title>
    <updated>2026-04-13T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2026-04-13-programadores-en-simcity/con-la-ia-los-programadores-ya-vivimos-en-sim-city/</id>
    <content type="html">&lt;p&gt;Si has jugado alguna vez SimCity, ya conoces a buen seguro algunas cosas de las que te voy a hablar. Si no, mejor échate una partida primero (aunque sea casual y en el móvil…) y luego vuelve por aquí a leer el post 😄&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-table-of-contents&quot;&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;si-eres-buen-jugador-de-sim-city-te-las-arreglaras-bien-con-la-ia-en-los-proximos-anos&quot; tabindex=&quot;-1&quot;&gt;Si eres buen jugador de SimCity, te las arreglarás bien con la IA en los próximos años&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2026-04-13-programadores-en-simcity/media/sc2000-general.jpg&quot; alt=&quot;sc2000-general.jpgCiudad en SimCity 2000&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Hay un momento en SimCity en el que dejas de intentar controlarlo todo al milímetro y empiezas a entender el juego.&lt;/p&gt;
&lt;p&gt;No construyes las casas con tus propias manos. No conduces los coches de la gente. No llevas cubos de agua para apagar un incendio. Lo que haces es &lt;strong&gt;diseñar el sistema&lt;/strong&gt;: zonas, infraestructuras, políticas, flujos… Y luego dejas que la ciudad funcione y crezca, ayudado por tus asesores. Tu papel es guiar y monitorizar todo, para intervenir y mejorar la ciudad (y ahí es cuando &lt;em&gt;juegas&lt;/em&gt; y te diviertes, vaya)&lt;/p&gt;
&lt;p&gt;Si en algún momento has liderado un equipo de ingeniería, estoy seguro que te suena.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://es.wikipedia.org/wiki/Andrew_Grove&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Andy Grove&lt;/a&gt; (Intel) formuló la clave con precisión hace décadas: &lt;em&gt;el output de un manager es el output de su equipo, más el de los equipos bajo su influencia&lt;/em&gt;. El manager no es el que más produce individualmente, es el que diseña el entorno donde otros producen más y multiplica su impacto.&lt;/p&gt;
&lt;p&gt;Y estoy convencido que exactamente lo mismo aplica hoy al desarrollador que trabaja con agentes de IA. Tu rol es hacer que el sistema para desarrollar software en tu empresa funcione y produzca lo mejor posible.&lt;/p&gt;
&lt;h2 id=&quot;el-problema-de-la-multitarea-propia&quot; tabindex=&quot;-1&quot;&gt;El problema de la multitarea (propia)&lt;/h2&gt;
&lt;p&gt;La multitarea es una trampa bien documentada. El coste de cambio de contexto es real, el foco se fragmenta y el resultado final es siempre peor que si hubieras hecho una cosa a la vez.&lt;/p&gt;
&lt;p&gt;Pero hay otro tipo de multitarea que nadie critica: la de &lt;em&gt;los demás trabajando en paralelo para ti&lt;/em&gt;, de forma bien coordinada.&lt;/p&gt;
&lt;p&gt;En SimCity, mientras tú lees un informe de tráfico, los bomberos apagan un fuego, los obreros terminan una carretera y los ciudadanos van al trabajo. No estás haciendo nada de eso en primera persona: tú estás pensando en el siguiente &lt;strong&gt;movimiento&lt;/strong&gt; &lt;strong&gt;estratégico&lt;/strong&gt; o bien atendiendo algún problema puntual &lt;strong&gt;urgente&lt;/strong&gt; que solo tú puedes resolver.&lt;/p&gt;
&lt;p&gt;Eso es exactamente lo que ocurre cuando tu equipo de software funciona bien. Y es exactamente lo que ocurre cuando los agentes de IA funcionan bien en tu proyecto: eres capaz de hacer más a la vez, porque delegas trabajo sustancial a los agentes de IA. Usa esa capacidad y los agentes, hasta el punto en el que sientas que controlas lo que haces (vigila tu carga cognitiva y nivel de energía), y esfuérzate en aprender cómo ampliar tu radio de impacto.&lt;/p&gt;
&lt;h2 id=&quot;gas-town-y-la-ia&quot; tabindex=&quot;-1&quot;&gt;Gas Town y la IA&lt;/h2&gt;
&lt;p&gt;En SimCity es habitual, como en el Urbanismo en general, diseñar un distrito industrial, especializado en producir energía y recursos para el resto de la ciudad. De nuevo, tú no gestionas cada tubería o conducción eléctrica hacia él, solo te aseguras de que la zona esté bien conectada, financiada y sin conflictos con las zonas residenciales.&lt;/p&gt;
&lt;p&gt;Y no creo que sea casualidad que a principios de 2026 &lt;em&gt;Steve Yegge&lt;/em&gt; haya creado un software llamado &lt;a href=&quot;https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;&lt;strong&gt;Gas Town&lt;/strong&gt;&lt;/a&gt;, un sistema para fabricar software con múltiples agentes IA orquestados, que pretender ser una alternativa a los IDEs tradicionales.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2026-04-13-programadores-en-simcity/media/gas-town.webp&quot; alt=&quot;gas-town.webpGas town&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Gas Town sigue unos principios similares a SimCity: un alcalde que coordina a otros agentes (también antropomorfizados), hay factorías, refinerías... y tú trabajando con el sistema en un &lt;strong&gt;plano de control&lt;/strong&gt;, necesariamente cada vez más abstracto y estratégico (como por otra parte siempre ha sucedido en el ámbito de la Informática).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Gas Town es (aún o tal vez será para siempre...) una herramienta bastante caótica y compleja y es una ciudad (perdón sistema) que gasta muchos tokens... Pero estoy convencido que varias de sus ideas guiarán las herramientas de creación de software en el futuro. Si te suena interesante, creo que merece la pena probarlo al menos y luego leer esta revisión crítica de &lt;em&gt;Maggie Appleton&lt;/em&gt;, del área de innovación en GitHub: &lt;a href=&quot;https://maggieappleton.com/gastown&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://maggieappleton.com/gastown&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;el-plano-de-control&quot; tabindex=&quot;-1&quot;&gt;El plano de control&lt;/h2&gt;
&lt;p&gt;En SimCity, el alcalde tiene un dashboard. No ve todo el tiempo cada tramo de tubería de gas, ve los indicadores que importan: tráfico, criminalidad, satisfacción ciudadana, presupuesto... Y actúa cuando algo requiere su atención directa e inmediata. Y probablemente el alcalde no se conforma con que su ciudad simplemente siga igual, sino que siempre tendrá uno o dos proyectos ambiciosos, para los que está preparando un plan y buscando financiación… así que trabaja en multitarea, repartido entre lo estratégico a medio largo plazo y lo operativo del día a día&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2026-04-13-programadores-en-simcity/media/sc2000-indicadores.jpg&quot; alt=&quot;sc2000-indicadores.jpgPantalla de presupuesto en SimCity 2000&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Y yo en mi día a día como desarrollador siento algo parecido: interactúo con agentes de IA y compañeros, buscando esas señales correctas para actuar y a la vez mantener un buen equilibrio entre la multitarea y sacar bloques sólidos de trabajo (sea un plan para una nueva feature, un bug en producción o una actualización rutinaria de seguridad).&lt;/p&gt;
&lt;p&gt;Actualmente uso herramientas variadas como la terminal con &lt;em&gt;tmux&lt;/em&gt; (si algo en tech lleva casi 20 años vivo y la gente lo sigue usando, merece la pena echar un ojo, efecto Lindy en estado puro), Claude Code web / desktop, &lt;a href=&quot;https://agents.craft.do/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Craft Agents&lt;/a&gt;, MCPs contra Honeycomb, Postgres o Slack, y mi uso del IDE se ha reducido mucho (ahora uso Cursor, pero todos son similares)… pero siento que aún no he encontrado la configuración / herramienta ideal. Sigo buscando y se aceptan sugerencias, por cierto...&lt;/p&gt;
&lt;p&gt;Estoy convencido que la industria de la IA y el desarrollo de software en general avanzarán en este plano de control pronto, es muy necesario.&lt;/p&gt;
&lt;p&gt;Pero también estoy seguro que las señales para la acción, los dashboard de indicadores clave, mejorarán mucho con la IA: de simples dashboard técnicos con cientos de variables y gráficos no relevantes + dashboards de negocio con datos del trimestre pasado, a dashboards generados por agentes, con datos en tiempo real y mucho menos ruido y más señal:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No &lt;em&gt;&amp;quot;El sistema funciona&amp;quot;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;No &lt;em&gt;&amp;quot;Hay 200 errores en el log&amp;quot;&lt;/em&gt;, y búscate la vida...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sino algo como &amp;quot;&lt;em&gt;Esta es el Top 4 de los problemas de esta semana en tu SaaS&lt;/em&gt;&amp;quot;, realizada con ayuda de agentes IA, por ejemplo:&lt;br /&gt;
- la latencia en la API XXX ha empeorado un 15% los últimos 10 días, y hay previsto el onboarding de un cliente grande a finales de mes, ¿arrancamos un proyecto de mejora?&lt;br /&gt;
- ¿Priorizamos la resolución de estos 5 bugs concretos, dado que afectan a 2 clientes con riesgo de churn que renuevan este mes?&lt;br /&gt;
- El tiempo medio de onboarding del último cliente ZZZ ha sido casi el doble de la media...¿ha comunicado algún problema concreto a customer success / ha habido algún bug o caída puntual de la plataforma que haya podido afectar a su experiencia?&lt;br /&gt;
- La competencia ha sacado esta feature el mes pasado, ¿merece la pena lanzar un prototipo rápido en nuestra plataforma para ver si somos capaces de incorporarla?&lt;/p&gt;
&lt;h2 id=&quot;la-crisis-del-programador-una-cuestion-de-identidad&quot; tabindex=&quot;-1&quot;&gt;La crisis del programador, una cuestión de identidad&lt;/h2&gt;
&lt;p&gt;Hay algo de lo que quizá no se habla suficientemente en esta conversación sobre IA y el futuro del trabajo técnico: &lt;strong&gt;la identidad&lt;/strong&gt; del trabajador inmerso en estos cambios. Tendemos a identificarnos con lo que hacemos: &lt;em&gt;soy programador&lt;/em&gt;. Y cuando lo que hacemos cambia radicalmente, la pregunta se vuelve incómoda: si ya no programas línea a línea, ¿qué eres?&lt;/p&gt;
&lt;p&gt;El término &lt;em&gt;programador&lt;/em&gt; tiene un anclaje muy concreto y una tradición histórica amplia: escribir código todo el día. Si el código lo generan ahora los agentes, el anclaje se rompe. Y con él, parte de tu identidad. Por eso mucha gente se siente perdida, con pena o incluso miedo a los cambios de la IA.&lt;/p&gt;
&lt;p&gt;Creo que se trata simplemente de abrir el foco y ver donde están ahora los verdaderos problemas, donde está &amp;quot;el juego&amp;quot;. Y los problemas grandes ya no están tanto en programar algo o aprender un lenguaje para crear un sistema; están en planificarlo, diseñarlo con una visión humana y sobre todo comprobar que funciona y sacarle valor a escala.&lt;/p&gt;
&lt;p&gt;Si necesitamos etiquetas, creo que hay otros términos que encajan mejor en esta etapa: ingeniero o simplemente desarrollador:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;ingeniero&lt;/em&gt;, en su sentido original, el de &amp;quot;ingeniárselas para resolver problemas&amp;quot;.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;desarrollador&lt;/em&gt;: el creador de una solución técnica, de una idea de negocio... en definitiva de un sistema que funciona.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Los ordenadores nos dan la base para abordar problemas cada vez más complejos. Pero lo que importa es sobre todo lo que construyes, no el ritual específico o herramienta con la que lo construyes.&lt;/p&gt;
&lt;p&gt;Creo que la IA no elimina al desarrollador. Elimina al &lt;em&gt;programador cuyo valor está solo en teclear&lt;/em&gt;. El que sobrevive y prospera es el que entiende sistemas, toma decisiones y sabe orquestar las herramientas IA disponibles hacia un resultado tangible y útil.&lt;/p&gt;
&lt;h2 id=&quot;cuando-lleguen-los-incendios&quot; tabindex=&quot;-1&quot;&gt;Cuando lleguen los incendios...&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2026-04-13-programadores-en-simcity/media/sc2000-incendios.webp&quot; alt=&quot;sc2000-incendios.webpIncendio en SimCity 2000&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Eso sí: prepárate. Porque de vez en cuando llegarán los incendios. En SimCity, los desastres no avisan. Incendio, terremoto, tornado o monstruo marino. De repente todo el sistema que habías diseñado necesita intervención directa, urgente y manual.&lt;/p&gt;
&lt;p&gt;Con los agentes de IA pasa igual. Habrá momentos en que el contexto falle, la coordinación se rompa o el output sea basura. Y ahí tienes que estar tú, con las manos en el código y un conocimiento suficientemente profundo de lo que tienes entre manos, como para operar y corregir el sistema.&lt;/p&gt;
&lt;p&gt;El objetivo no es desaparecer del sistema. Es tener el sistema lo mejor construido posible, y que tu intervención sea la excepción, no la regla.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[!Aprendizaje]&lt;br /&gt;
El conocimiento aplicable tradicionalmente se ha forjado &amp;quot;en las trincheras&amp;quot;, mediante la práctica de la construcción de esos sistemas de forma más manual... si eso no se produce ahora durante la producción, hay que aprender igualmente; consejo: lee, experimenta, pregunta y re-pregunta... usa la propia IA para aclararte cualquier duda que puedas tener sobre lo que tus compañeros o tú mismo estáis generando en el código. Si te saltas ese proceso, alguien pagará el coste.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Todo lo que vivimos ahora mismo en el mundo del software no es sino la avanzadilla de lo que en breve inundará toda profesión y no creo que tenga sentido tenerle miedo o intentar postponer lo inevitable.&lt;/p&gt;
&lt;p&gt;Una de las personas que más admiro en el mundo del software es &lt;em&gt;Kent Beck&lt;/em&gt; (creador de XP, TDD...). Bueno, pues este señor, con 65 años no para de experimentar y publicar cómo trabaja cada día mejor con la IA (o como el la llama &amp;quot;the Genie&amp;quot;) para potenciar su trabajo... así que no hay excusas.&lt;/p&gt;
&lt;p&gt;Abraza el cambio que supone la IA, el impacto de lo que vas a poder hacer se va a multiplicar, no tengas miedo.&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2026-04-13-programadores-en-simcity/con-la-ia-los-programadores-ya-vivimos-en-sim-city/"/>
  </entry><entry>
    <title>Obsidian como herramienta para un cerebro digital</title>
    <updated>2025-02-19T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2025-02-19-herramientas-gestion-conocimiento/obsidian-como-herramienta-para-un-cerebro-digital/</id>
    <content type="html">&lt;p&gt;Me gusta anotar cosas, es así. Y se podría decir que le dedico, según la temporada, un cierto esfuerzo. Tanto a hacerlo como a revisar o mejorar cómo lo hago (qué escribo, con que herramientas, cómo organizo mis notas…).&lt;/p&gt;
&lt;h2 id=&quot;por-que-lo-hago&quot; tabindex=&quot;-1&quot;&gt;¿Por qué lo hago?&lt;/h2&gt;
&lt;p&gt;Bueno pues simplemente porque creo que pienso más y mejor cuando escribo. En general no escribo cosas muy largas, ni tampoco “literarias” (ya me gustaría… ¿a lo mejor algún día?). Se trata de notas generalmente no muy extensas, sobre algo que me interesa, proyectos/tareas, investigaciones técnicas, notas sobre un libro que me gusta… y, cada vez más, &amp;quot;notas reflexivas” sobre lo que pienso y que me ayudan a tomar decisiones.&lt;/p&gt;
&lt;p&gt;A veces tener algo escrito y a la mano me sirve para contestar o compartir algo de interés con otros, es cierto, pero la mayoría de las veces escribo para mi. Escribir me centra, a veces me desahoga y me sirve para cosas del trabajo, cosas de casa, cosas más personales… y estoy descubriendo poco a poco lo potente que es releer / reescribir / completar / borrar cierto tipo de notas… (sí, también borrar, no me seas Diógenes).&lt;/p&gt;
&lt;p&gt;Lo de “cerebro digital” suena un poco a marketing barato la verdad, quizá me gusta más algo como “sistema de notas”, pero es el término por el que puedes buscar más info al respecto si te interesa el tema.&lt;/p&gt;
&lt;p&gt;Pero bueno, este post pretende recoger un par de consejos sobre una herramienta y un sistema / libro, más que hablar de las bondades de escribir, así que al grano.&lt;/p&gt;
&lt;h2 id=&quot;obsidian&quot; tabindex=&quot;-1&quot;&gt;Obsidian&lt;/h2&gt;
&lt;p&gt;En los últimos años he pasado por distintas herramientas (Evernote, Notion, libretas en papel…). Todas han tenido para mi su momento y sus ventajas, pero he ido migrando. Hace años pasé de Evernote a Notion, buscando más flexibilidad y features, pero al final ambas comparten muchos principios: modelo cloud de subscripción anual, formatos cerrados, búsquedas, tablas... Por cierto, la migración de notas de una a otra fue un dolor y tuve que invertir bastante tiempo en revisar / importar manualmente cosas; esto no es casual claro, las apps quieren mantenerte cautivo.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;¿Por que Obsidian?&lt;/strong&gt;&lt;br /&gt;
Como programador,  la herramienta encaja bien con mi modelo mental y los principios de control / libertad / flexibilidad que valoro, porque respeta en gran medida el principio de separación &lt;strong&gt;modelo vs. vista&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Las notas serían el modelo, en un formato markdown que no me tiene cautivo en la herramienta (las puedo editar sin problema con otro software y sobre todo sigo teniendo en mi equipo, en claro, todos los ficheros y carpetas, bajo una carpeta base (que se identifica como “vault”). La aplicación (y sus muchos plug-in gratuitos) serían la “vista” y proporcionan todo tipo de features extra (búsquedas, etiquetas, tablas, tareas, calendario, hasta publicación web por ejemplo para crear un blog…), pero lo hacen sin contaminar las notas, por encima de ellas. Sobre la parte específica de publicación quizá escriba en el futuro, porque es de hecho lo que estoy utilizando para escribir y publicar este post, de forma 100% gratuita.&lt;/p&gt;
&lt;p&gt;¿La desventaja principal? &lt;a href=&quot;https://obsidian.md/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Obsidian&lt;/a&gt; es gratuito pero no software libre. Hay una empresa detrás que lo mantiene, y monetiza con un servicio propio de sincronización entre dispositivos. Si lo fuera, sería 10 de 10. Aún así, parece que hay bastante comunidad activa alrededor y me parece que le quedan muchos años por delante. Lo que es seguro es que si algún día migro a otra cosa, mis notas serán muchísimo más fáciles de portar.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plugins&lt;/strong&gt;&lt;br /&gt;
Hay un montón de &lt;em&gt;Community plugins&lt;/em&gt;, pero los imprescindibles para mí son (la mayoría tienen nombres auto-explicativos):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Advanced Tables&lt;/em&gt; (by Tony Grosinger)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Automatic Table of Contents&lt;/em&gt; (by Johan Satgé)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Calendar&lt;/em&gt; (by Liam Cain)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Dataview&lt;/em&gt; (by Michael Brenan). Una pequeña joya para generar listas y tablas de forma flexible y automáticamente a partir de otras notas, mediante una sintaxis parecida a un mini-sql&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Excalidraw&lt;/em&gt; (by Zsolt Viczian). Para todo tipo de diagramas, aunque si te gusta más el formato Mermaid es aceptado tal cual por Obsidian&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Importer&lt;/em&gt;. Lo usé para migrar de Notion a Obsidian y me funcionó de maravilla.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Sincronización / dispositivos&lt;/strong&gt;&lt;br /&gt;
Esto también me gusta, porque hay varias opciones. Hay una opción de pago del propio Obsidian, para móviles, tablets… pero no la he probado. En mi caso particular estoy utilizando la sincronización que ya pagaba por otro lado de iCloud (así que me ahorro el pago a Notion); a veces es un poco lenta... pero me arreglo. Se que también hay opciones para usar Google Drive, e incluso git.&lt;/p&gt;
&lt;h2 id=&quot;sistema-de-notas-cerebro-digital&quot; tabindex=&quot;-1&quot;&gt;Sistema de notas: “cerebro digital”&lt;/h2&gt;
&lt;p&gt;Lo quieras o no, si escribes mucho acabarás necesitando algún tipo de sistema. En mi opinión no hace falta hacerse un máster, montar catedrales, ni tampoco aplicar a ciegas algo que un tipo de USA te dice que es lo mejor. Lo mejor es ir poco a poco y crear algo que te funcione a ti. Ahora bien, leer un poco al respecto no te hará mal, no eres la primera persona en el mundo organizando notas digitales y hay mucha gente lista por ahí.&lt;/p&gt;
&lt;p&gt;En mi caso particular, leí un libro que recomiendo bastante. Se trata de “&lt;em&gt;Aprende a tomar notas con tu Cerebro Digital: Leer es perder el tiempo si luego no puedes recuperar esa información&lt;/em&gt;” de Marcos Emowe. Título un poco largo, pero libro bastante claro y con buenas ideas.&lt;/p&gt;
&lt;p&gt;Estas son algunas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No guardés lo que no vayas a usar (como en bases de datos 😄). Parece obvio, pero luego no lo es tanto.&lt;/li&gt;
&lt;li&gt;Lo que sirve es lo que haces con las notas, no las notas (el conocimiento y la acción). Para los que se detienen más en afilar el hacha que en cortar.&lt;/li&gt;
&lt;li&gt;El sistema de notas es distinto a la herramienta (no los mezcles!). Mantente lo más autónomo posible y en control de tu información.&lt;/li&gt;
&lt;li&gt;Dale más peso a las conexiones entre notas (modelo “cerebro”). Esto complementará a los modos que seguramente ya te sean más naturales   a la hora de tomar notas: modelo ”biblioteca” (títulos, jerarquía, tipo apuntes-libro…) y modelo ”rejilla” (tablas, datos numéricos…).&lt;/li&gt;
&lt;li&gt;Procura hacer notas modulares. Hay varias referencias interesantes al sistema llamado Zettlekasten. Por cierto, este es otro principio que aplica al diseño de software, por eso me gusta :).&lt;/li&gt;
&lt;li&gt;Tipos de notas&lt;/li&gt;
&lt;li&gt;Como tomar notas y mejorarlas.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Algunas quizá te pueden parecer obvias, otras seguramente son nuevas… pero si te planteas un cambio en tu sistema / herramienta te lo recomiendo.&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2025-02-19-herramientas-gestion-conocimiento/obsidian-como-herramienta-para-un-cerebro-digital/"/>
  </entry><entry>
    <title>Working effectively with legacy code</title>
    <updated>2018-01-29T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2018-01-29-working-effectively-with-legacy-code/working-effectively-with-legacy-code/</id>
    <content type="html">&lt;p&gt;Hace poco he terminado de leer &lt;em&gt;Working effectively with legacy code,&lt;/em&gt; de &lt;em&gt;Michael Feathers&lt;/em&gt;, un libro clásico de programación y, como me ha gustado mucho, me he decidido a compartir unas notas sobre él, por si a alguien le pica el gusanillo de leerlo. Forma parte de la colección &amp;quot;Robert C. Martin Series&amp;quot;, en el que existen otros libros muy interesantes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2018-01-29-working-effectively-with-legacy-code/images/legacycode1.jpg&quot; alt=&quot;legacycode1.jpg&quot; /&gt;&lt;br /&gt;
Es un libro muy práctico, especialmente si programas con orientación a objetos, pero su título quizá es poco afortunado. En realidad, se trata de un libro que habla sobre todo de &lt;strong&gt;Tests y Refactoring&lt;/strong&gt;, y de cómo usar ambas técnicas para trabajar con &lt;em&gt;Legacy Code (&amp;quot;código legado&amp;quot;)&lt;/em&gt;, algo que muchos programadores tratan de evitar siempre que pueden.&lt;/p&gt;
&lt;p&gt;El libro está organizado en tres bloques:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;Las mecánicas del cambio&lt;/em&gt;: en el que presenta unos conceptos generales y herramientas de trabajo.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Cambiando el software&lt;/em&gt;: un &amp;quot;catálogo de recetas&amp;quot; ante problemas concretos&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Técnicas para romper dependencias&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;las-mecanica-del-cambio&quot; tabindex=&quot;-1&quot;&gt;Las mecánica del cambio&lt;/h2&gt;
&lt;p&gt;El libro comienza hablando de las causas principales que motivan los cambios en un software: añadir una funcionalidad, corregir un error, mejorar el diseño u optimizar el uso de un recurso.&lt;/p&gt;
&lt;p&gt;De aquí me quedo con una reflexión que hace el autor, que me parece muy valiosa, y es que da igual la causa final, probablemente el mayor reto para el programador será &lt;strong&gt;mantener el comportamiento existente,&lt;/strong&gt; algo que ilustra con esta imagen&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2018-01-29-working-effectively-with-legacy-code/images/existingbehaviour.png&quot; alt=&quot;ExistingBehaviour&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Sobre la forma en la que habitualmente hacemos los cambios, el autor critica lo que ya conocemos: muchas veces se usa el arriesgado &lt;em&gt;&amp;quot;Edit and Pray&amp;quot;&lt;/em&gt; (editar el código y rezar porque nuestro esfuerzo sea acertado), cuando lo más adecuado sería aplicar &lt;em&gt;&amp;quot;Cover and Modify&amp;quot;&lt;/em&gt;, es decir cubrir primero el código con tests y, solo después, comenzar a modificarlo.&lt;/p&gt;
&lt;p&gt;Los test cumplen aquí una doble misión: actuar como red de seguridad y proporcionar un feedback rápido sobre nuestro conocimiento del sistema, el cual crecerá con ellos.&lt;/p&gt;
&lt;p&gt;De hecho, la definición de &amp;quot;Legacy Code&amp;quot; por parte del autor es &amp;quot;&lt;strong&gt;un código Legacy es aquel que no tiene tests&lt;/strong&gt;&amp;quot; (y por tanto es difícil de modificar), algo que si nos paramos a pensar tiene todo el sentido del mundo. Tener buenos tests en un proyecto es una señal de su salud, pero la realidad es que muchas veces no existen y que añadirlos no es fácil. Entonces ¿cómo comenzar a trabajar con un código sin tests e incorporarlos?&lt;/p&gt;
&lt;p&gt;El libro sugiere seguir estos pasos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1. Identificar los puntos de cambio&lt;/strong&gt;: busquemos primero donde debemos cambiar el código para cumplir nuestros objetivos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2. Encontrar puntos donde crear tests&lt;/strong&gt;: hay que valorar los más adecuados para probar los cambios en marcha (no podemos empezar probando todo indiscriminadamente).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3. Romper las dependencias&lt;/strong&gt;: éstas suelen ser el principal obstáculo para crear los tests. Para eliminarlas, los objetos de tipo &lt;em&gt;fake&lt;/em&gt; / &lt;em&gt;mock&lt;/em&gt; pueden sernos muy útiles. Nos servirán no solo para &lt;em&gt;separar&lt;/em&gt; las clases que queremos probar sino también para &lt;em&gt;sentir&lt;/em&gt; a través de ellos que el comportamiento del objeto bajo test es el adecuado.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;4. Escribir los tests&lt;/strong&gt;: si se trata de cubrir primero código existente, con un comportamiento no evidente, podemos crear &lt;em&gt;tests de caracterizacion&lt;/em&gt;, que simplemente describen lo que hace actualmente ante unos inputs y nos permitirán detectar si cambiamos involuntariamente su comportamiento más adelante. Otro concepto interesante para los tests es el de &amp;quot;costura&amp;quot; o &lt;em&gt;s__eam:&lt;/em&gt; un lugar en el que se puede &amp;quot;modificar&amp;quot; el comportamiento de un método sin tocar su código. Por ejemplo si el método recibe como parámetro un objeto e invoca un método suyo, podríamos en un test pasar una instancia de ese objeto en el que el método haya sido reescrito, a conveniencia del test.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;5. Hacer cambios  y refactorizar&lt;/strong&gt;, apoyados en herramientas de refactoring automático.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;cambiando-el-software&quot; tabindex=&quot;-1&quot;&gt;Cambiando el software&lt;/h2&gt;
&lt;p&gt;Este bloque del texto sigue un modelo de &amp;quot;recetas&amp;quot;, planteando consejos y técnicas de refactoring a una lista extensa de problemas. Por citar algunos de los ejemplos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;¿Cómo añado una funcionalidad?&lt;/li&gt;
&lt;li&gt;Necesito hacer un cambio, pero no se qué tests escribir&lt;/li&gt;
&lt;li&gt;Mi aplicación es todo llamadas a un API&lt;/li&gt;
&lt;li&gt;¿Cómo sé que no estoy rompiendo nada?&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tecnicas-para-romper-dependencias&quot; tabindex=&quot;-1&quot;&gt;Técnicas para romper dependencias&lt;/h2&gt;
&lt;p&gt;Presenta varias técnicas de forma detallada y con código de ejemplo, algunas adaptadas del libro &lt;em&gt;Refactoring&lt;/em&gt;, de &lt;em&gt;Martin Fowler&lt;/em&gt;. Técnicas que, una vez aplicadas, rompen dependencias y favorecen la creación de tests.&lt;/p&gt;
&lt;p&gt;Algunas de ellas son por ejemplo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Extraer interfaz:&lt;/em&gt; si una clase existente es conflictiva para un test, podemos derivar de ella un interfaz y acoger selectivamente algunos de sus métodos. De esta forma, luego podremos sustituirla por otra clase que implemente ese mismo interfaz, pero que incluya las modificaciones del comportamiento que nos interesen.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Parametrizar un constructor&lt;/em&gt;: si una dependencia se crea dentro de un constructor, una solución sencilla es exponer ese objeto como parámetro. Esta misma técnica puede generalizarse también a métodos comunes.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Crear una subclase y sobreescribir un método&lt;/em&gt;: es una técnica potente, basada en usar herencia dentro de un test, para anular o modificar con una subclase la porción de comportamiento que nos interese. Personalmente me parece una técnica muy útil y que no conocía.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;—-&lt;/p&gt;
&lt;p&gt;En resumen, creo que el libro merece la pena por el mensaje que trasmite: Legacy no es &amp;quot;un código que otro ha hecho mal hace mucho tiempo&amp;quot;, puede ser un módulo que acabamos de escribir nosotros mismos sin crear unos tests asociados. Y es valioso también por el catálogo de recetas del segundo bloque, algo a lo que personalmente seguro que volveré buscando consejo en el futuro.&lt;/p&gt;
&lt;p&gt;Si te enfrentas a actualizar un sistema antiguo, sin duda éste es un libro muy útil para ti.&lt;/p&gt;
&lt;p&gt;Saludos&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2018-01-29-working-effectively-with-legacy-code/working-effectively-with-legacy-code/"/>
  </entry><entry>
    <title>Jornadas SIG Libre Girona</title>
    <updated>2017-06-05T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2017-06-05-jornadas-sig-libre-girona-2017/jornadas-sig-libre-girona/</id>
    <content type="html">&lt;p&gt;La semana pasada tuve la suerte de poder acudir de nuevo a las Jornadas de SIG Libre de Girona, en su edición nº 11, esta vez presentando un desarrollo técnico, mi plugin para Leaflet, llamado &lt;strong&gt;Leaftlet.CanvasLayer.Field,&lt;/strong&gt; para pintar rasters y campos vectoriales.&lt;/p&gt;
&lt;video width=&quot;640&quot; controls=&quot;&quot;&gt;
  &lt;source src=&quot;https://bibstream.udg.edu/streaming/4802.mp4&quot; type=&quot;video/mp4&quot; /&gt;
  Your browser does not support the video tag.
&lt;/video&gt;
&lt;p&gt;Aquí está el programa completo de las Jornada, como referencia: &lt;a href=&quot;http://www.sigte.udg.edu/jornadassiglibre/programa/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://www.sigte.udg.edu/jornadassiglibre/programa/&lt;/a&gt; (colgarán más adelante los vídeos de las ponencias). Estaba revisando mis notas, para hacer balance y compartirlo con mis compañeros, pero he pensado que quizá sería más interesante hacerlo públicamente, así que aquí están.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2017-06-05-jornadas-sig-libre-girona-2017/media/jsl11-girona2017.jpg&quot; alt=&quot;jsl11-girona2017.jpg&quot; /&gt;&lt;/p&gt;
&lt;h1 id=&quot;ponencias-plenarias&quot; tabindex=&quot;-1&quot;&gt;Ponencias plenarias&lt;/h1&gt;
&lt;p&gt;Tras la apertura oficial por parte de la Universidad, comenzaron las Jornadas con la presencia de &lt;em&gt;Rosa M. Badia&lt;/em&gt; (Barcelona Supercomputing Center - BSC). Dejando de lado las cifras brutales de cálculo previstas para el Mare Nostrum 4, lo que me llamó la atención fue la importancia que le prestaba no al hardware, sino a los &lt;em&gt;modelos de programación&lt;/em&gt; y cómo indicaba la mezcla progresiva, en enfoques y herramientas, entre Supercomputación y BigData. Aunque no hubo referencia alguna a temas geoespaciales, Rosa citó &lt;a href=&quot;https://pypi.python.org/pypi/pycompss&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;PyCOMPSs&lt;/a&gt; un framework libre en Python para favorecer el desarrollo de modelos en paralelo en estos entornos, el cual parece interesante.&lt;/p&gt;
&lt;p&gt;A continuación &lt;em&gt;Lluís Sanz&lt;/em&gt; (Instituto Municipal de Informática del Ayuntamiento de Barcelona) hizo un repaso general de la estrategia &lt;a href=&quot;http://ajuntament.barcelona.cat/estrategiadigital/es&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Barcelona Ciudad Digital&lt;/a&gt;. En el ámbito SIG, ya en la parte final, comentó que están en un proceso de migración de bases de datos y herramientas diversas a QGIS, en un proyecto que se llama &lt;em&gt;QVista&lt;/em&gt; y que será liberado este año, lo cual es una gran noticia. Si esto se cumple, podríamos disponer de un código aplicable también a otros ayuntamientos.&lt;/p&gt;
&lt;p&gt;Terminó el bloque de plenarias &lt;em&gt;David Comas&lt;/em&gt;, de Nexus Geographics, todo un referente en los SIG. Con una visión muy clara, lanzó algunas ideas-clave: (1) lo generalista en los SIG ya está solucionado, hay que especializarse y (2) los SIG, con interfaces de alto valor, deben responder preguntas concretas y sencillas y así brindar tiempo al usuario experto para resolver él los temas complejos.&lt;/p&gt;
&lt;h1 id=&quot;comunicaciones&quot; tabindex=&quot;-1&quot;&gt;Comunicaciones&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Frederic Bartumeus (CREAF) presentó &lt;a href=&quot;http://www.mosquitoalert.com/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Mosquito Alert&lt;/a&gt;, una plataforma de ciencia ciudadana para la lucha contra el mosquito tigre y las enfermedades que transmite. Muy interesante caso de cómo ligar ciudadanos, científicos y gestores públicos gracias a una aplicación de base geoespacial. Creo que en el futuro se desarrollarán muchas más iniciativas de este tipo y ojalá que parte de su código sea reutilizable para ello (&lt;a href=&quot;https://github.com/MoveLab&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;github&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tras él, &lt;em&gt;María Arias de Reyna&lt;/em&gt; habló sobre las pocas mujeres, y en general poca diversidad, que existe en el ámbito tecnológico y cómo esto supone una clara pérdida de riqueza y variedad de enfoques. Presentó como luchar contra esta situación gracias a iniciativas como &lt;a href=&quot;https://twitter.com/pingmujeres?lang=es&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;@pingmujeres&lt;/a&gt; y llamó la atención sobre los aspectos educativos, señalando la necesidad de despertar más vocaciones &lt;a href=&quot;https://es.wikipedia.org/wiki/Educaci%C3%B3n_STEM&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;STEM&lt;/a&gt; en niñas y adolescentes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Ramiro Aznar&lt;/em&gt; (Carto), mostró después 3 casos tipo en los que la representación con mapas es habitualmente difícil y los resultados obtenidos generalmente malos, junto con propuestas para mejorarlas: * muchos puntos, de distintas categorías, generando &amp;quot;empaste&amp;quot; por estar próximos y/o superpuestos. Cuando esto sucede, aconseja reducir las categorías y jugar con la ordenación visual (categorías menos abundantes arriba) y la transparencia (los registros más escasos, arriba, más transparentes). * puntos en el mismo lugar. Para hacer visible lo oculto se pueden usar varias alternativas, como mapas de calor, clusters, &lt;a href=&quot;https://carto.com/blog/stacking-chips-a-map-hack&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;stacking chips&lt;/a&gt; o agregación de puntos. * muchos campos para un solo registro. Aquí es conveniente pensar no en un mapa estático, sino en visualizaciones interactivas, en las que con controles como los de &lt;em&gt;Builder&lt;/em&gt; se pueda aplicar de forma rápida y sencilla filtrado y estilos dinámicos. La verdad es que en esto Carto brilla especialmente.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Santiago Higuera&lt;/em&gt; presentó después el estado de OpenStreetMap España y cómo el proyecto sigue con impulso, con unos 100-110 usuarios activos al día y aprox. 30.000 nodos/día modificados en España.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tras la pausa para la comida, continuaron las sesiones. A partir de aquí había doble &#39;track&#39; así que sólo recogeré el bloque al que acudí (llamado &amp;quot;Investigación&amp;quot;):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Víctor Olaya&lt;/em&gt; (Boundless) presentó algunos consejos interesantes para los profesionales del SIG, tanto desarrolladores como usuarios / analistas. Básicamente, se centraban en la construcción por parte de cada uno de un &amp;quot;portfolio&amp;quot; público, perdiendo el miedo a la vergüenza, en el que se demuestren las capacidades e intereses, sea a través de foros, listas de correo, blogs o repositorios github.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Iván Sánchez&lt;/em&gt; mostró después las posibilidades que se abren al aplicar &lt;a href=&quot;https://gitlab.com/IvanSanchez/Leaflet.TileLayer.GL&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;WebGL&lt;/a&gt; a mapas teselados en Leaflet. Un trabajo innovador y que muestra una gran velocidad de pintado pixel a pixel, abriendo la puerta a visualizaciones y análisis raster en el navegador. Creo que el futuro del pintado de mapas en web pasa claramente por trabajos con WebGL como este.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Eduard Suñé&lt;/em&gt; (IDESCAT) explicó con detalle cómo crear grids de población útiles, mediante quadtrees, a la vez que se protege el necesario secreto estadístico. Un ámbito que profesionalmente no conozco, pero que me hizo reflexionar sobre el esfuerzo necesario para encontrar un equilibrio entre dato geográfico de máxima calidad y el derecho a la privacidad de las personas.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Santiago Higuera&lt;/em&gt; dió luego un repaso a las opciones de referenciación lineal, ejemplificando en la red de carreteras españolas; se repasaron algunos aspectos de la ISO19148 y las opciones presentes en SIG de escritorio libres y PostGIS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Jose Gómez Castaño&lt;/em&gt; (INSPIDE), autor de la app &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.inspide.comobity&amp;amp;hl=es&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Comobity&lt;/a&gt;, describió la arquitectura de su plataforma &lt;em&gt;Singularity&lt;/em&gt; para la DGT, donde tecnologías de Big Data como Spark, Hadoop, Flume o Kafka sirven para explotar datos en tiempo real y contribuir a una mayor seguridad en las vías públicas. Creo que ésta es una línea muy clara de futuro del sector geo: aplicaciones geográficas en un mercado vertical muy claro, con grandes volúmenes de datos y productos geográficos (no solo mapas) en tiempo real.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;César Díaz&lt;/em&gt; (Tracasa Instrumental) mostró por su parte cómo utilizan unas tecnologías SIG muy contrastadas para crear mapas temáticos, en el Ayuntamiento de Pamplona (OpenLayers + Geoserver + PostGIS, con vistas parametrizadas, WMS &amp;amp; SLD); sin duda un &#39;stack clásico&#39;, pero muy efectivo.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Por la tarde, continué en las charlas del track &amp;quot;Desarrollos técnicos&amp;quot;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Roger Veciana&lt;/em&gt; (Meteocat), presentó algo novedoso: cómo aplicar D3js como herramienta de render de datos raster meteorológicos, permitiendo eliminar el proceso de generación automática de tiles en el servidor cada día. Con esto el cliente web gana en flexibilidad y capacidad de cambiar estilos al vuelo. Una desarrollo muy interesante, como lo es blog de su autor &lt;a href=&quot;http://geoexamples.com/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;geoexamples&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Después de Roger, nosotros presentamos el trabajo que hemos hecho en el IHCantabria: &lt;a href=&quot;https://github.com/IHCantabria/Leaflet.CanvasLayer.Field/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Leaflet.CanvasLayer.Field&lt;/a&gt;, un plugin para crear animaciones de viento / corrientes y que también renderiza raster, esta vez sobre Leaflet.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Aquí la &lt;a href=&quot;https://victorvelarde.github.io/presentacion-jsl11/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;presentación completa&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;http://victorvelarde.github.io/presentacion-jsl11&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2017-06-05-jornadas-sig-libre-girona-2017/media/pluginleaflet.png&quot; alt=&quot;pluginleaflet.pngpluginLeaflet&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Y tras nosotros, el mismo Roger, esta vez a título personal, presentó una extensión llamada &lt;a href=&quot;https://github.com/rveciana/d3-composite-projections&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;d3-composite-projections&lt;/a&gt;, la cual permite visualizar áreas disjuntas en un solo mapa (p.ej. Canarias &#39;cerca&#39; de la Península). Muy práctica, p.ej. para mapas electorales.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Ernesto Martínez&lt;/em&gt; (Carto) dió por su parte un repaso a las novedades de esta plataforma en 2017, siendo la principal la migración a Builder para todos los usuarios &lt;em&gt;free&lt;/em&gt;. Otro aspecto que personalmente me llamó la atención fue la creciente importancia de los datos pre-cargados en la plataforma para enriquecer los análisis (&lt;em&gt;Data Observatory&lt;/em&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Josep Lluís Sala&lt;/em&gt; (Giswater Association) contó el estado de la aplicación y de la red de asociados a este proyecto. Es un mercado en el que parece que la solución, basada en QGIS, PostGIS y modelos hidráulicos libres, parece estar convirtiéndose en estándar, lo cual es una gran noticia.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;Ivan Sánchez&lt;/em&gt; presentó después una llamativa charla, llamada &amp;quot;Índices Miriahédricos Fractales Cuaternarios Para Fenómenos Geográficos&amp;quot;, que escondía un curioso uso de palabras como índices geográficos (básicamente un &lt;em&gt;what3words&lt;/em&gt;, pero con tacos).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Y finalmente &lt;em&gt;María Arias de Reyna&lt;/em&gt; mostró el estado de GeoNetwork, un CMS &#39;geográfico&#39; con mucha solera para trabajar con metadatados, al que claramente le han dado un buen empujón en lo visual.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;talleres&quot; tabindex=&quot;-1&quot;&gt;Talleres&lt;/h1&gt;
&lt;p&gt;Además de las comunicaciones, tuve la oportunidad de acudir a dos talleres:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;El primero sobre Docker, impartido por &lt;em&gt;Joana Simoes&lt;/em&gt; y &lt;em&gt;Jorge de Jesús&lt;/em&gt;. &lt;em&gt;Docker&lt;/em&gt; parece una herramienta muy interesante para facilitar la gestión de servicios en producción, no solo para gente &amp;quot;de sistemas&amp;quot;, sino también para desarrolladores. Me gustó la iniciativa que presentaron, &lt;a href=&quot;https://hub.docker.com/u/geocontainers/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;geocontainers&lt;/a&gt;, con algunos servicios SIG empaquetados y preparados para usar, y una herramienta visual para el control de contenedores llamada &lt;a href=&quot;http://portainer.io/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;portainer&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;El segundo taller, sobre desarrollo de plugins para QGIS, fue impartido por &lt;em&gt;Luigi Pirelli&lt;/em&gt;. Estuvo centrado en las herramientas necesarias para iniciar este camino: la consola básica, &lt;a href=&quot;https://github.com/elpaso/qgis-ipythonconsole&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;IPyConsole&lt;/a&gt;, &lt;a href=&quot;https://github.com/g-sherman/Qgis-Plugin-Builder&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Plugin Builder&lt;/a&gt;, &lt;a href=&quot;https://github.com/sourcepole/qgis-remote-debug&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Remote Debug&lt;/a&gt; y &lt;a href=&quot;https://github.com/borysiasty/plugin_reloader&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Plugin Reloader&lt;/a&gt;. Existen documentos que ayudan en el cambio de la v2 a la v3, para ayudar en el proceso de &lt;a href=&quot;http://slides.com/luigipirelli/desdarrolloplugindeqgis-xi-girona#/18&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;migración de plugins&lt;/a&gt;. Como consejo interesante: está previsto liberar QGIS 3 para finales de 2017 y parece bueno dejar aún madurar un poco las APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;resumen&quot; tabindex=&quot;-1&quot;&gt;Resumen&lt;/h1&gt;
&lt;p&gt;A modo de resumen, diría que tengo la sensación que el mercado SIG Libre, y con él las Jornadas, ha madurado, con empresas, tecnologías y usuarios consolidados. Quizá eché en falta alguna presentación más sobre JavaScript o nuevas tendencias como BigData, drones... pero creo que los contenidos en general han sido buenos, la organización y el ambiente inmejorables, y espero poder repetir el próximo año.&lt;/p&gt;
&lt;p&gt;¡Saludos!&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2017-06-05-jornadas-sig-libre-girona-2017/jornadas-sig-libre-girona/"/>
  </entry><entry>
    <title>Brackets un editor ágil para desarrollo web</title>
    <updated>2017-02-28T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2017-02-28-brackets-un-editor-agil-para-desarrollo-web/brackets-un-editor-agil-para-desarrollo-web/</id>
    <content type="html">&lt;p&gt;En los últimos años ha habido una explosión de editores de texto, especialmente en el ámbito del desarrollo con JavaScript: &lt;a href=&quot;https://www.sublimetext.com/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Sublime&lt;/a&gt;, &lt;a href=&quot;https://atom.io/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Atom&lt;/a&gt;, &lt;a href=&quot;https://code.visualstudio.com/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Code&lt;/a&gt; o el propio &lt;a href=&quot;http://brackets.io/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Brackets&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Todos excepto Sublime son software libre y gratuito y tienen un enfoque similar: editores rápidos y con menos funciones &amp;quot;de serie&amp;quot; (comparados con grandes IDE tradicionales, como p.ej. Eclipse, Visual Studio...). Estos editores poseen además un ecosistema de plugins y temas muy amplio, para que cada uno personalice a su gusto sus funciones y apariencia.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2017-02-28-brackets-un-editor-agil-para-desarrollo-web/media/brackets_ide.jpg&quot; alt=&quot;brackets_ide&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Después de usarlo en varios proyectos, &lt;strong&gt;Brackets&lt;/strong&gt; me parece una opción interesante, así que comparto aquí la lista de plugins que utilizo actualmente por si os resulta útil:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Brackets-Git&lt;/strong&gt;: manejo de git&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Beautify&lt;/strong&gt;: formateo automático de HTML, JS y CSS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Indent Guides&lt;/strong&gt;: como guía visual para seguir la apertura-cierre de secciones&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minimap&lt;/strong&gt;: guía del documento en pequeño, para desplazarse por él&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Newline&lt;/strong&gt;: para controlar el uso de CRLF &amp;lt;-&amp;gt; LF como fin de línea (windows / linux)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WordCount&lt;/strong&gt;: nº de palabras&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Brackets Css Color Preview&lt;/strong&gt;: mostrar el color escrito en un CSS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EMMET&lt;/strong&gt;: agilizar la escritura de HTML&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Markdown Preview&lt;/strong&gt;: para visualizar ficheros .md&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Brackets Tree Icons&lt;/strong&gt;: para otorgar un icono a cada fichero según su tipo&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Brackets File Tabs&lt;/strong&gt;: para complementar el árbol lateral con pestañas&lt;/li&gt;
&lt;/ul&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2017-02-28-brackets-un-editor-agil-para-desarrollo-web/brackets-un-editor-agil-para-desarrollo-web/"/>
  </entry><entry>
    <title>Visor de terremotos del USGS con OpenLayers</title>
    <updated>2016-01-20T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2016-01-20-visor-de-terremotos-del-usgs-con-openlayers/visor-de-terremotos-del-usgs-con-open-layers/</id>
    <content type="html">&lt;p&gt;En este post se presenta un pequeño ejemplo de visor, en el que se muestran los terremotos más recientes en el mundo, usando la biblioteca &lt;em&gt;OpenLayers 3&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2016-01-20-visor-de-terremotos-del-usgs-con-openlayers/media/thumbnail.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Los terremotos se leen de un origen remoto, en formato &lt;em&gt;Geojson&lt;/em&gt;, y se actualizan automáticamente, gracias a un estupendo servicio &lt;em&gt;feed&lt;/em&gt; del &lt;a href=&quot;http://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;USGS&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;El visor puede verse funcionando, gracias a &lt;em&gt;&lt;a href=&quot;http://bl.ocks.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;bl.ocks.org&lt;/a&gt;&lt;/em&gt; aquí: &lt;a href=&quot;http://bl.ocks.org/VictorVelarde/raw/5021e3679dad1305d570/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://bl.ocks.org/VictorVelarde/raw/5021e3679dad1305d570/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Y en cuanto al codigo HTML y JavaScript, es sencillo y puede verse en este &lt;em&gt;gist&lt;/em&gt;: &lt;a href=&quot;https://gist.github.com/VictorVelarde/5021e3679dad1305d570&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://gist.github.com/VictorVelarde/5021e3679dad1305d570&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;

&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Ejemplo sencillo con OL3&amp;lt;/title&amp;gt;
    &amp;lt;link href=&amp;quot;http://fonts.googleapis.com/css?family=Open+Sans|Dosis:400,800&amp;quot; rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; /&amp;gt;

    &amp;lt;!-- Versión específica vs. master | min vs. debug --&amp;gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;http://openlayers.org/en/v3.12.1/css/ol.css&amp;quot; type=&amp;quot;text/css&amp;quot;&amp;gt;
    &amp;lt;script src=&amp;quot;http://openlayers.org/en/v3.12.1/build/ol-debug.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;style&amp;gt;
        /* General */
        
        h1 {
            font-family: &#39;Dosis&#39;, sans-serif;
            font-weight: 400;
            font-size: 40px;
            line-height: 46px;
            margin-bottom: 10px;
            color: &lt;a class=&quot;tag&quot; data-content=&quot;#E50275&quot;&gt;#E50275&lt;/a&gt;;
        }
        
        body {
            font-family: &#39;Open Sans&#39;, sans-serif;
            background-color: &lt;a class=&quot;tag&quot; data-content=&quot;#F7FBFF&quot;&gt;#F7FBFF&lt;/a&gt;;
        }
        
        .ficha {
            color: &lt;a class=&quot;tag&quot; data-content=&quot;#03A1C4&quot;&gt;#03A1C4&lt;/a&gt;;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Mapa de terremotos significativos el último mes (USGS)&amp;lt;/h1&amp;gt;

    &amp;lt;div id=&amp;quot;mapa&amp;quot; style=&amp;quot;height: 400px; border: 2px gray solid;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;div id=&amp;quot;seleccion&amp;quot; style=&amp;quot;margin-top:10px&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;

    &amp;lt;!-- JavaScript --&amp;gt;
    &amp;lt;script&amp;gt;
        var mapa = new ol.Map({
            layers: [new ol.layer.Tile({
                source: new ol.source.OSM()
            })],
            target: &#39;mapa&#39;,
            view: new ol.View({
                center: [0, 0],
                zoom: 2
            })
        });

        mapa.addControl(new ol.control.OverviewMap());

        // carga de datos
        var urlTerremotos = &#39;http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/significant_month.geojson&#39;;
        var capaTerremotos = new ol.layer.Vector({
            source: new ol.source.Vector({
                url: urlTerremotos,
                format: new ol.format.GeoJSON()
            })
        });
        mapa.addLayer(capaTerremotos);

        // interacción capa vectorial
        var seleccion = new ol.interaction.Select();
        mapa.addInteraction(seleccion);
        seleccion.on(&#39;select&#39;, function (e) {
            // ol.interaction.SelectEvent
            var div = document.getElementById(&#39;seleccion&#39;);
            var seleccionados = e.target.getFeatures();
            var html = &#39;&#39;;
            seleccionados.forEach(function (t) {
                html += &#39;&amp;lt;div class=&amp;quot;ficha&amp;quot;&amp;gt;&amp;lt;ul&amp;gt;&#39;;
                html += &#39;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Lugar&amp;lt;/strong&amp;gt;: &#39; + t.get(&#39;place&#39;) + &#39;&amp;lt;/li&amp;gt;&#39;;
                html += &#39;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Magnitud&amp;lt;/strong&amp;gt;: &#39; + t.get(&#39;mag&#39;) + &#39;&amp;lt;/li&amp;gt;&#39;;
                html += &#39;&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;Fecha&amp;lt;/strong&amp;gt;: &#39; + new Date(t.get(&#39;time&#39;)).toLocaleDateString() + &#39;&amp;lt;/li&amp;gt;&#39;;
                html += &#39;&amp;lt;/ul&amp;gt;&amp;lt;/div&amp;gt;&#39;;
            });
            div.innerHTML = html;
        });
    &amp;lt;/script&amp;gt;

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sobre gist &amp;amp; bl.ocks&lt;/strong&gt;: * &lt;em&gt;&lt;a href=&quot;https://gist.github.com/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;gist&lt;/a&gt;&lt;/em&gt;. Se trata de un servicio gratuito de github, para alojar pequeños codigos de ejemplo, que se persisten sobre un repositorio &lt;em&gt;git&lt;/em&gt;. Gracias a ello son accesibles para su modificación desde cualquier equipo con un cliente git. * &lt;em&gt;&lt;a href=&quot;http://bl.ocks.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;bl.ocks.org&lt;/a&gt;&lt;/em&gt;. Un servicio, del creador de D3, que siguiendo unos pocos convenios en el gist, permite el acceso a su código en vivo, junto a su fuente, permitiendo mini-proyectos autodocumentados.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Nota&lt;/em&gt;: Este ejemplo se presenta en el último video de la serie de seminarios sobre JavaScript, elaborada para &lt;a href=&quot;http://geospatialtraining.com/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;geospatialtraining.com&lt;/a&gt; (ver &lt;a href=&quot;https://victorvelarde.wordpress.com/2016/01/12/videos-introduccion-a-javascript/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Videos de introducción a JavaScript&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://youtu.be/ik6F38PPBYw&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Video explicativo&lt;/a&gt;&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2016-01-20-visor-de-terremotos-del-usgs-con-openlayers/visor-de-terremotos-del-usgs-con-open-layers/"/>
  </entry><entry>
    <title>6 videos de Introducción a JavaScript</title>
    <updated>2016-01-12T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2016-01-12-videos-introduccion-a-javascript/6-videos-de-introduccion-a-java-script/</id>
    <content type="html">&lt;p&gt;Hola a todos,&lt;/p&gt;
&lt;p&gt;En esta entrada os presento los 6 vídeos gratuitos de un curso introductorio a JavaScript, realizado en colaboración con &lt;a href=&quot;http://geospatialtraininges.com/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://geospatialtraininges.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Los vídeos no asumen conocimientos previos en la materia y están orientados a personas del mundo SIG que quieren iniciarse en la programación web.&lt;/p&gt;
&lt;p&gt;Espero que os sean útiles.&lt;/p&gt;
&lt;p&gt;Saludos!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Módulo I: Variables y datos&lt;/strong&gt; (41 min) &lt;a href=&quot;https://www.youtube.com/watch?v=%5C_qFvPJHGuCw&amp;amp;feature=youtu.be&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://www.youtube.com/watch?v=&#92;_qFvPJHGuCw&amp;amp;feature=youtu.be&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Módulo II: Funciones y Bucles&lt;/strong&gt; (1h) &lt;a href=&quot;https://www.youtube.com/watch?v=UEQpIkrQv78&amp;amp;feature=youtu.be&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://www.youtube.com/watch?v=UEQpIkrQv78&amp;amp;feature=youtu.be&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Módulo III: Arrays y objetos&lt;/strong&gt; (1h 12min) &lt;a href=&quot;https://www.youtube.com/watch?v=1kVJDyx5FK8&amp;amp;feature=youtu.be&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://www.youtube.com/watch?v=1kVJDyx5FK8&amp;amp;feature=youtu.be&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Módulo IV: DOM&lt;/strong&gt; (1h 3min) &lt;a href=&quot;https://www.youtube.com/watch?v=UQXEmqSOQK8&amp;amp;feature=youtu.be&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://www.youtube.com/watch?v=UQXEmqSOQK8&amp;amp;feature=youtu.be&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Módulo V: Bibliotecas y frameworks&lt;/strong&gt; (43 min) &lt;a href=&quot;https://www.youtube.com/watch?v=TW7lr0QDNd0&amp;amp;feature=youtu.be&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://www.youtube.com/watch?v=TW7lr0QDNd0&amp;amp;feature=youtu.be&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Módulo VI: APIs SIG de JavaScript&lt;/strong&gt; (1h 23min) &lt;a href=&quot;https://www.youtube.com/watch?v=23X7GqLi5oQ&amp;amp;feature=youtu.be&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://www.youtube.com/watch?v=23X7GqLi5oQ&amp;amp;feature=youtu.be&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2016-01-12-videos-introduccion-a-javascript/6-videos-de-introduccion-a-java-script/"/>
  </entry><entry>
    <title>Recursos para aprender QGIS</title>
    <updated>2015-02-24T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2015-02-24-recursos-para-aprender-qgis/recursos-para-aprender-qgis/</id>
    <content type="html">&lt;p&gt;&lt;em&gt;QGIS&lt;/em&gt; es uno los software SIG de escritorio más pujantes en la actualidad. Libre, gratuito y extensible mediante plugins, tiene un potencial de evolución grande y una comunidad de usuarios creciente.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www2.qgis.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2015-02-24-recursos-para-aprender-qgis/media/logo-qgis.png&quot; alt=&quot;logo-qgis.pngQGIS&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Por esta razón cada día más gente se acerca a su aprendizaje, bien mediante cursos o de forma autónoma. Y en este proceso inicial toda guía o recopilación de enlaces suele resultar útil, así que voy a listar aquí algunos que he ido recopilando recientemente.&lt;/p&gt;
&lt;h2 id=&quot;enlaces-basicos&quot; tabindex=&quot;-1&quot;&gt;Enlaces básicos&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.qgis.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Página QGIS&lt;/a&gt;: El sitio oficial, con multitud de recursos y enlaces derivados.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://planet.qgis.org/planet/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;QGIS Planet&lt;/a&gt;: Un agregador de varios blogs que recoge unificadamente artículos técnicos sobre QGIS (como p.ej. los publicados en &lt;em&gt;Free and Open Source GIS Ramblings&lt;/em&gt;, de &lt;a href=&quot;https://twitter.com/underdarkgis&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Anita Graser&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://plugins.qgis.org/plugins/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;QGIS Plugins&lt;/a&gt;: Listado oficial de plugins&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hub.qgis.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;QGIS Hub&lt;/a&gt;: Wiki oficial del proyecto, donde p.ej. se reportan los bugs y recogen peticiones para futuras funcionalidades.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://qgis.org/api/2.6/index.html&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;QGIS API&lt;/a&gt;: Documentación del API oficial, un recurso básico para programadores.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://gis.stackexchange.com/questions/3651/where-can-i-find-qgis-tutorials-and-resources&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;QGIS en stackexchange&lt;/a&gt;: Artículo en stackexchange donde se listan varios recursos.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://gis.stackexchange.com/questions/tagged/qgis&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;QGIS en gis.stackexchange&lt;/a&gt;: Consultas sobre QGIS en gis.stackexchange&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tutoriales-gratuitos&quot; tabindex=&quot;-1&quot;&gt;Tutoriales gratuitos&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.qgis.org/2.6/en/docs/training_manual/index.html&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;QGIS Training Manual&lt;/a&gt;: Manual de aprendizaje de QGIS, en formato de curso con ejercicios prácticos (incluye p.ej. apartados sobre uso de la base de datos espacial &lt;em&gt;PostgreSQL/PostGIS&lt;/em&gt; o &lt;em&gt;GRASS&lt;/em&gt;). Como complemento a este material existe un conjunto de videos en español, que aparece listado en la &lt;a href=&quot;http://cv.uoc.edu/webapps/xwiki/wiki/sistemasinformaciongeoqgis/view/XWiki/GuiaVideosQGIS&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;siguiente wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.qgis.org/2.2/es/docs/gentle_gis_introduction/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;A gentle introduction to GIS&lt;/a&gt;: Una introducción práctica y breve a los SIG que no asume conocimientos previos. Está basada en el tutorial original &lt;a href=&quot;http://linfiniti.com/dla/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Introducing GIS worksheets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.youtube.com/playlist?list=PLNCPalajQvg7wQvzf3fM8fOZ5lMKl86Q4&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;QGIS in under 10 minutes&lt;/a&gt;: Videotutoriales cortos con operaciones habituales en QGIS (serie de 17 vídeos, en inglés)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.youtube.com/playlist?list=PLyVgyXwS_ZDDhxheaxKUh31BCD0Azg1of&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Curso de QGIS Valmiera&lt;/a&gt;: Videotutoriales QGIS, versión 2.2 (serie de 10 vídeos, en castellano).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://canvas.instructure.com/courses/855327&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;GST 101: Introduction to Geospatial Technology&lt;/a&gt;. Curso de introducción a SIG, usando QGIS 2.2&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://canvas.instructure.com/courses/861361&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;GST 102: Introduction to Geospatial Technology&lt;/a&gt;. Continuación del curso previo, más centrado en la parte de análisis espacial con QGIS.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Saludos y espero que resulten útiles.&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2015-02-24-recursos-para-aprender-qgis/recursos-para-aprender-qgis/"/>
  </entry><entry>
    <title>Cómo utilizar datos geográficos públicos de la NASA (SRTM) con un software libre como QGIS</title>
    <updated>2015-01-23T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2015-01-23-como-utilizar-datos-geograficos-publicos-de-la-nasa-srtm-con-un-software-libre-como-qgis/como-utilizar-datos-geograficos-publicos-de-la-nasa-srtm-con-un-software-libre-como-qgis/</id>
    <content type="html">&lt;p&gt;Si en una &lt;a href=&quot;https://victorvelarde.wordpress.com/2014/12/18/como-utilizar-datos-geograficos-publicos-de-openstreetmap-con-un-software-libre-como-qgis/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;entrada previa&lt;/a&gt; mostrábamos algunos ejemplos de uso de datos vectoriales libres, procedentes de OpenStreetMap, en este caso vamos a revisar una valiosa fuente de datos cuasi-mundial con la altitud del terreno denominada &lt;em&gt;Shuttle Radar Topography Mission&lt;/em&gt; (SRTM).&lt;/p&gt;
&lt;p&gt;Dentro del mundo SIG, a estas fuentes de datos se las denomina &lt;em&gt;Modelos Digitales del Terreno (MDT)&lt;/em&gt; y recogen, generalmente en forma de fichero, una estructura numérica de datos que representa la distribución espacial de una variable, sea en forma vectorial (contornos o TIN) o más frecuentemente raster (matrices regulares o &lt;em&gt;quadtrees&lt;/em&gt;). Si esta variable es la altitud, entonces hablamos de &lt;em&gt;Modelos Digitales de Elevaciones&lt;/em&gt; (MDE).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Para profundizar en las bases conceptuales de los MDT, recomiendo los estupendos materiales de A.M. Felicísimo en &lt;a href=&quot;http://www6.uniovi.es/~feli/CursoMDT/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://www6.uniovi.es/~feli/CursoMDT/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;La SRTM que nos ocupa es una misión comandada por la NASA desarrollada en el año 2000, que mediante su transbordador espacial Endeavour y un radar aerotransportado tomó datos en detalle sobre la elevación terrestre, entre los 60º de latitud norte y los 56º de latitud sur (más detalles técnicos de la misión en la &lt;a href=&quot;http://www2.jpl.nasa.gov/srtm/mission.htm&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;página oficial SRTM&lt;/a&gt;). Si bien algunos de sus datos son públicos desde hace varios años, desde septiembre de 2014 han comenzado a liberarse por primera vez los lotes más detallados, con la resolución original de aproximadamente 30 metros (1 arco-segundo), antes solo disponibles en USA.&lt;/p&gt;
&lt;p&gt;En esta entrada veremos algunas posibilidades del uso combinado de un software SIG libre como &lt;a href=&quot;http://www.qgis.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;QGIS&lt;/a&gt; y los datos de esta fuente.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;h2 id=&quot;como-empezar-a-trabajar-con-qgis-y-srtm&quot; tabindex=&quot;-1&quot;&gt;¿Cómo empezar a trabajar con QGIS y SRTM?&lt;/h2&gt;
&lt;p&gt;Asumiendo que ya tenemos QGIS correctamente instalado (instrucciones &lt;a href=&quot;http://www.qgis.org/es/site/forusers/download.html&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;aquí&lt;/a&gt;), procederemos primero a la descarga de datos &lt;a href=&quot;https://lta.cr.usgs.gov/SRTMBasic&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;SRTM Basic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Para la descarga usaremos una herramienta interactiva proporcionada por el USGS (Servicio Geológico de Estados Unidos) llamada &lt;strong&gt;EarthExplorer&lt;/strong&gt; (&lt;a href=&quot;http://earthexplorer.usgs.gov/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://earthexplorer.usgs.gov/&lt;/a&gt;). Desde ella se puede descargar no solo la SRTM, sino una gran variedad de fuentes. Antes deberemos registrarnos como usuarios &lt;a href=&quot;https://earthexplorer.usgs.gov/register/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;aquí&lt;/a&gt; (un proceso gratuito, aunque nos llevará un par de minutos).&lt;/p&gt;
&lt;p&gt;Luego seguiremos los sencillos pasos marcados por el asistente:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1. Enter Search Criteria&lt;/code&gt;: en nuestro caso navegaremos en el mapa hasta encuadrar el ámbito de la Bahía de Santander, en Cantabria.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2015-01-23-como-utilizar-datos-geograficos-publicos-de-la-nasa-srtm-con-un-software-libre-como-qgis/media/srtm_01.png&quot; alt=&quot;srtm_01.png&quot; /&gt;&lt;br /&gt;
Fig.1: Selección del ámbito de interés en EarthExplorer&#92;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;2. Select Your Data Set(s)&lt;/code&gt;, donde indicaremos la palabra clave &#39;SRTM&#39; para filtrar los datos y poder llegar al producto &lt;em&gt;&#39;SRTM 1 Arc-Second Global&#39;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2015-01-23-como-utilizar-datos-geograficos-publicos-de-la-nasa-srtm-con-un-software-libre-como-qgis/media/srtm_02.png&quot; alt=&quot;srtm_02.png&quot; /&gt;&lt;br /&gt;
Fig.2: Filtro de dataset SRTM&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Al pulsar en la búsqueda aparecerán las hojas disponibles en el apartado &lt;code&gt;4. Search Results&lt;/code&gt;. En nuestro caso son dos hojas y procederemos a la descarga de la identificada como &lt;em&gt;SRTM1N43W004V3&lt;/em&gt;, en formato &lt;a href=&quot;http://es.wikipedia.org/wiki/GeoTIFF&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;&lt;em&gt;GeoTIFF&lt;/em&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2015-01-23-como-utilizar-datos-geograficos-publicos-de-la-nasa-srtm-con-un-software-libre-como-qgis/media/srtm_03.png&quot; alt=&quot;srtm_03.png&quot; /&gt;&lt;br /&gt;
Fig.3: Descarga de hoja SRTM 1 Arc-Second Global&lt;/p&gt;
&lt;p&gt;El fichero descargado &lt;em&gt;n43_w004_1arc_v3.tif&lt;/em&gt; podremos ya cargarlo en &lt;strong&gt;QGIS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Para ello bastará con utilizar la opción del menú &lt;code&gt;Capa - Añadir capa raster&lt;/code&gt; y se visualizará con la escala de colores por defecto, en tonos grises. Podremos también consultar la altitud del terreno registrada en cada punto, haciendo clic con la herramienta &lt;code&gt;Identificar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2015-01-23-como-utilizar-datos-geograficos-publicos-de-la-nasa-srtm-con-un-software-libre-como-qgis/media/srtm_04.png&quot; alt=&quot;srtm_04.png&quot; /&gt;&lt;br /&gt;
Fig.4: MDE de SRTM en QGIS, con paleta gris unibanda&lt;/p&gt;
&lt;p&gt;Una vez cargados los datos, podemos comenzar a explotarlos utilizando distintas herramientas de QGIS. A continuación mostraremos brevemente 3 ejemplos.&lt;/p&gt;
&lt;h2 id=&quot;ejemplo-1-usar-srtm-como-mapa-base&quot; tabindex=&quot;-1&quot;&gt;Ejemplo 1. Usar SRTM como mapa base&lt;/h2&gt;
&lt;p&gt;El MDE de la SRTM constituye una gran base para la visualización topográfica, y puede servir como mapa de fondo sobre el que representar después nuestra información geográfica particular (p.ej. una ruta tomada con el GPS). Una forma rápida de dar un estilo mejorado a la capa raster, es mediante &lt;code&gt;Raster - Análisis del terreno - Relieve&lt;/code&gt;, que genera una capa con visualización de sombreado y tintas hipsométricas simultáneas.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2015-01-23-como-utilizar-datos-geograficos-publicos-de-la-nasa-srtm-con-un-software-libre-como-qgis/media/srtm_05.png&quot; alt=&quot;srtm_05.png&quot; /&gt;&lt;br /&gt;
Fig.5: MDE SRTM sombreado en QGIS&lt;br /&gt;
 &lt;/p&gt;
&lt;h2 id=&quot;ejemplo-2-usar-srtm-para-una-visualizacion-3-d&quot; tabindex=&quot;-1&quot;&gt;Ejemplo 2. Usar SRTM para una visualización 3D&lt;/h2&gt;
&lt;p&gt;QGIS dispone de un plugin muy interesante para la visualización en 3D de un MDE en un simple navegador web. El plugin se llama &lt;em&gt;Qgis2threejs&lt;/em&gt;, que utiliza internamente la librería de JavaScript &lt;em&gt;threejs&lt;/em&gt;. Con multitud de opciones para explorar, es una herramienta de visualización / publicación web de resultados muy interesante.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2015-01-23-como-utilizar-datos-geograficos-publicos-de-la-nasa-srtm-con-un-software-libre-como-qgis/media/srtm_06.png&quot; alt=&quot;srtm_06.png&quot; /&gt;&lt;br /&gt;
Fig.6: MDE SRTM en vista 3D con Qgis2threejs&lt;/p&gt;
&lt;h2 id=&quot;ejemplo-3-usar-srtm-para-un-analisis-de-ubicacion-optima&quot; tabindex=&quot;-1&quot;&gt;Ejemplo 3. Usar SRTM para un análisis de ubicación óptima&lt;/h2&gt;
&lt;p&gt;El hecho de conocer la altitud en cada celda (cada 30 metros), unido a las capacidades analíticas de QGIS para datos raster, permite usar SRTM en potentes análisis geográficos. Por ejemplo, es posible modelizar el flujo del agua y estudiar cómo se configuran las cuencas hidrográficas o calcular las horas potenciales de insolación anuales en un punto...&lt;/p&gt;
&lt;p&gt;En el siguiente vídeo mostraremos otro ejemplo aplicado a un problema SIG clásico: la búsqueda de la mejor ubicación, apoyados en la &lt;em&gt;calculadora raster&lt;/em&gt; de QGIS:&lt;br /&gt;
&lt;a href=&quot;http://vimeo.com/112688478&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://vimeo.com/112688478&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;Esta entrada es una colaboración en el blog iNFoRMáTICa++, perteneciente a los Estudios de Informática, Multimedia y Telecomunicación (EIMT) de la Universitat Oberta de Catalunya (UOC). Publicada originalmente en: &lt;a href=&quot;http://informatica.blogs.uoc.edu/2015/01/15/como-utilizar-datos-geograficos-publicos-de-la-nasa-srtm-con-un-software-libre-como-qgis/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://informatica.blogs.uoc.edu/2015/01/15/como-utilizar-datos-geograficos-publicos-de-la-nasa-srtm-con-un-software-libre-como-qgis/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2015-01-23-como-utilizar-datos-geograficos-publicos-de-la-nasa-srtm-con-un-software-libre-como-qgis/como-utilizar-datos-geograficos-publicos-de-la-nasa-srtm-con-un-software-libre-como-qgis/"/>
  </entry><entry>
    <title>3 lenguajes de programación para SIG</title>
    <updated>2015-01-07T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2015-01-07-3-lenguajes-de-programacion-para-sig/3-lenguajes-de-programacion-para-sig/</id>
    <content type="html">&lt;p&gt;A lo largo de los años dentro del mundo SIG es habitual, para un analista o usuario avanzado, encontrarse en la posición de tener que decidir: &lt;em&gt;¿en qué tecnología/s formarme?&lt;/em&gt; O dicho de otra forma ¿a qué dedico mi valioso (y probablemente escaso) tiempo-presupuesto? ¿aprendo a manejar un nuevo SIG de escritorio, a gestionar un servidor SIG... o a qué?&lt;/p&gt;
&lt;p&gt;Esta primera pregunta hoy día tiene una sólida y clara respuesta: primero &lt;strong&gt;aprende a programar&lt;/strong&gt; (y si ya sabes, aprende a programar mejor). Tomando las palabras de James Fee (al que recomiendo seguir en su &lt;a href=&quot;http://jamesfee.us/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;blog&lt;/a&gt;): &lt;em&gt;&amp;quot;GIS analysis has become programming and development&amp;quot;&lt;/em&gt;, así que la cuestión es más bien... &lt;strong&gt;¿en qué lenguaje/s de programación invierto?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Aunque la lista de lenguajes candidatos es potencialmente extensa, para obtener el máximo retorno del esfuerzo lo más adecuado hoy día es centrare en &lt;strong&gt;SQL, Python y JavaScript&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;¿Por qué estos lenguajes y no otros?&lt;/p&gt;
&lt;h2 id=&quot;structured-query-language-sql&quot; tabindex=&quot;-1&quot;&gt;Structured Query Language (SQL)&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2015-01-07-3-lenguajes-de-programacion-para-sig/media/sql.png&quot; alt=&quot;sql.png&quot; /&gt;&lt;br /&gt;
&lt;strong&gt;SQL&lt;/strong&gt; (1974): lleva cuarenta años con nosotros y sigue vigente. Aunque estrictamente no es un lenguaje de programación, sino un &#39;lenguaje de consultas estructurado&#39;, lo importante es que todo SIG lleva un motor de consultas interno que permite, en mayor o menor medida, aplicar SQL. Si sabes bien los fundamentos, te será mucho más fácil aplicar luego los operadores espaciales disponibles para enriquecerlo con la componente &amp;quot;geo&amp;quot;. Acabarás teniendo un SHP, una plataforma en la nube como CartoDB, un PostGIS o un miserable Access, pero lo que es seguro es que si trabajas con SIG tendrás que manejar con soltura SQL.&lt;/p&gt;
&lt;h2 id=&quot;python&quot; tabindex=&quot;-1&quot;&gt;Python&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2015-01-07-3-lenguajes-de-programacion-para-sig/media/python.png&quot; alt=&quot;python.png&quot; /&gt;&lt;br /&gt;
&lt;strong&gt;Python&lt;/strong&gt; (1991): el lenguaje para muchos más adecuado para iniciarse en la programación, con librerías que facilitan el trabajo en prácticamente todo tipo de ámbitos (interfaces, multimedia, bioinformática...). En el mundo SIG la decisión ya está tomada hace un tiempo, desde el momento en que potentes librerías como &lt;em&gt;GDAL/OGR&lt;/em&gt; crearon sus envoltorios para Python y ESRI &amp;amp; QGIS lo adoptaron como lenguaje de geoprocesamiento. Python es hoy el lenguaje por excelencia para trabajar con SIG, especialmente a nivel de geoprocesamiento y en equipos de escritorio (otro tema es la construcción de soluciones web en servidor, donde el entorno Java con ejemplos como Geoserver-GeoTools aún sigue vigente).&lt;/p&gt;
&lt;h2 id=&quot;java-script&quot; tabindex=&quot;-1&quot;&gt;JavaScript&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2015-01-07-3-lenguajes-de-programacion-para-sig/media/javascript.png&quot; alt=&quot;javascript.png&quot; /&gt;&lt;br /&gt;
&lt;strong&gt;JavaScript&lt;/strong&gt; (1995): Tiene la capacidad de dotar de interacción a las páginas web e incorporar mapas y otras funciones SIG en ellas mediante bibliotecas (como OpenLayers, Leaflet, etc.), hasta conseguir aplicaciones SIG completas en el navegador web. Prácticamente toda la revolución de la &amp;quot;neogeografía&amp;quot;, que tanto ha beneficiado a los SIG estos últimos años, ha venido de su mano (primero con GoogleMaps, y más recientemente con bibliotecas como las de CartoDB, Mapbox...). De hecho, ahora comienzan a aparecer librerías JavaScript como &lt;a href=&quot;http://turfjs.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Turf&lt;/a&gt; para algo impensable hace unos años: realizar cálculos SIG en el navegador web (buffer, intersección, isolíneas...), sin tener que recurrir a un servidor dedicado de respaldo como ArcGIS Server o Geoserver.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;JavaScript no &amp;quot;viaja solo&amp;quot;: en la práctica hay que considerar un lote de 3 lenguajes, puesto que lleva aparejado lidiar con sus dos lenguajes &#39;hermanos&#39;: &lt;strong&gt;HTML y CSS&lt;/strong&gt;, que lo apoyan para construir aplicaciones web.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;haz-tu-mix&quot; tabindex=&quot;-1&quot;&gt;Haz tu mix&lt;/h2&gt;
&lt;p&gt;Si cada uno de estos lenguajes ya es útil por si sólo, lo son más en combinación. Aprovechar las sinergias que surgen entre ellos permite explotar y construir sistemas SIG más potentes (p.ej. usar Python en ArcGIS o QGIS y realizar filtros SQL contra una base de datos PostGIS, usar JavaScript y realizar consultas SQL a través de un API contra una tabla remota en CartoDB, etc.).&lt;/p&gt;
&lt;p&gt;Así que mi consejo es: 1. aprende los fundamentos básicos de los 3 &lt;strong&gt;lenguajes&lt;/strong&gt; 2. dedica el tiempo a profundizar en ellos y en nuevas &lt;strong&gt;bibliotecas&lt;/strong&gt; de funciones que los enriquezcan. 3. mejora durante el proceso tus &lt;strong&gt;habilidades generales&lt;/strong&gt; de programación (algoritmos, diseño, herramientas y prácticas ágiles...).&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2015-01-07-3-lenguajes-de-programacion-para-sig/3-lenguajes-de-programacion-para-sig/"/>
  </entry><entry>
    <title>Cómo utilizar datos geográficos públicos de OpenStreetMap con un software libre como QGIS</title>
    <updated>2014-12-18T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2014-12-18-como-utilizar-datos-geograficos-publicos-de-openstreetmap-con-un-software-libre-como-qgis/como-utilizar-datos-geograficos-publicos-de-open-street-map-con-un-software-libre-como-qgis/</id>
    <content type="html">&lt;p&gt;Analizar datos espaciales y generar con ellos mapas atractivos para el usuario es la gran fortaleza de los Sistemas de Información Geográfica (SIG). Y si hace unos pocos años el software SIG y los datos de calidad estaban sólo al alcance de unos pocos (grandes corporaciones, militares, universidades...), hoy cualquier profesional bien formado tiene en su mano la posibilidad de usarlos de forma rápida y gratuita. En esta entrada veremos algunas posibilidades del uso combinado de un software SIG libre como &lt;a href=&quot;http://www.qgis.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;QGIS&lt;/a&gt; y los datos, también libres, de &lt;a href=&quot;http://www.openstreetmap.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;OpenStreetMap&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;como-empezar-a-trabajar-con-qgis-y-osm&quot; tabindex=&quot;-1&quot;&gt;¿Cómo empezar a trabajar con QGIS y OSM?&lt;/h2&gt;
&lt;p&gt;Instalando en nuestros equipos la última versión de QGIS Desktop dispondremos de un software SIG de escritorio completo, gratuito y extensible mediante plugins (instrucciones de instalación detalladas de la v2.6, para Linux, Windows y Mac, en &lt;a href=&quot;http://www.qgis.org/es/site/forusers/download.html&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://www.qgis.org/es/site/forusers/download.html&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;OpenStreetMap (OSM) no es sólo un mapa: es una base de datos mundial con más de 30 GB de datos geográficos vectoriales de información muy diversa y detallada (carreteras, caminos, edificios, restaurantes, parques naturales...). En las últimas versiones QGIS integra de forma nativa la opción de descarga de datos OSM, desde su menú &lt;code&gt;Vectorial - OpenStreetMap (OSM) - Descargar Datos&lt;/code&gt;. Esto simplifica y agiliza la descarga de una porción de datos OSM, en forma de fichero vectorial &lt;code&gt;.osm&lt;/code&gt; (XML).&lt;/p&gt;
&lt;p&gt;En este ejemplo, utilizaremos datos de la ciudad de Santander, dentro del área geográfica definida manualmente por las siguientes coordenadas: &lt;code&gt;xmin:-3.8313931 ymin: 43.4449263 | xmax:-3.7658341 ymax: 43.4784917&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;QGIS también permite descargar datos OSM dentro del encuadre de una capa preexistente o a partir del zoom actual por pantalla. En cualquier caso, es importante que el sistema de referencia sea WGS84 Longitud / Latitud (EPSG: 4326) o el proceso no funcionará correctamente. Esto es debido a que internamente el complemento hace uso del &lt;a href=&quot;http://wiki.openstreetmap.org/wiki/Overpass_API&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;API Overpass&lt;/a&gt;, que recibe como parámetros las coordenadas del encuadre en este sistema.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Una vez descargados los datos OSM (en un fichero &lt;em&gt;santander.osm&lt;/em&gt;, de aprox. 5 MB), se pueden cargar directamente en QGIS, mediante la opción de menú &lt;code&gt;Capa - Añadir capa - Añadir capa vectorial&lt;/code&gt; (y luego seleccionando &lt;em&gt;points / lines / multipolygons&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2014-12-18-como-utilizar-datos-geograficos-publicos-de-openstreetmap-con-un-software-libre-como-qgis/media/osm_base.png&quot; alt=&quot;osm_base.png&quot; /&gt;&lt;br /&gt;
Fig.1: Datos OSM con los estilos por defecto en QGIS&lt;/p&gt;
&lt;p&gt;Una vez obtenidos los datos podemos comenzar el proceso de explotación y análisis, utilizando distintas herramientas de QGIS. A continuación mostraremos brevemente 3 ejemplos ilustrativos:&lt;/p&gt;
&lt;h2 id=&quot;ejemplo-1-usar-osm-para-generar-un-map&quot; tabindex=&quot;-1&quot;&gt;Ejemplo 1. Usar OSM para generar un map&lt;/h2&gt;
&lt;p&gt;Es posible tomar los datos OSM y generar con QGIS un mapa 100% personalizado, seleccionando cada entidad a mostrar, su etiquetado, su estilo... hasta obtener un mapa base o mapa temático a nuestra medida.&lt;/p&gt;
&lt;p&gt;QGIS proporciona un mecanismo para persistir reglas de visualización mediante ficheros de estilos reutilizables (&lt;em&gt;.qml&lt;/em&gt;). Por ejemplo, existen algunos .qml públicos que permiten dar una apariencia &#39;googlemaps&#39; a un lote de datos .OSM y si los aplicamos a nuestras capas OSM (menú de capa &lt;code&gt;Propiedades - Cargar estilo&lt;/code&gt;) el resultado es un mapa base como el siguiente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2014-12-18-como-utilizar-datos-geograficos-publicos-de-openstreetmap-con-un-software-libre-como-qgis/media/osm_estilos.png&quot; alt=&quot;osm_estilos.png&quot; /&gt;&lt;br /&gt;
Fig.2: Mismos datos OSM, con los estilos osm_spatialite_googlemaps_.qml, tomados de &lt;a href=&quot;https://github.com/anitagraser/QGIS-resources/tree/master/qgis2/osm%5C_spatialite%5C&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://github.com/anitagraser/QGIS-resources/tree/master/qgis2/osm&#92;_spatialite&#92;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Los .qml usados son ficheros para la versión 2.4 de QGIS y no se contemplan todos los tipos de objetos OSM, como p.ej. &#39;playas&#39; (categoría &lt;em&gt;natural:beach&lt;/em&gt;), con lo cual para un resultado óptimo deben trabajarse más los estilos.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;ejemplo-2-usar-osm-para-un-analisis-de-redes&quot; tabindex=&quot;-1&quot;&gt;Ejemplo 2. Usar OSM para un análisis de redes&lt;/h2&gt;
&lt;p&gt;OSM incluye un subconjunto especialmente relevante de datos que es la red viaria. OSM recoge el grafo conectado de autopistas, carreteras, caminos, calles... y permite por tanto aplicar los algoritmos propios del análisis de redes: camino más corto, estimación de tiempos para recorridos, etc.&lt;/p&gt;
&lt;p&gt;Aunque existen herramientas más sofisticadas, QGIS proporciona una práctica utilidad para calcular la ruta más corta en una red, mediante su complemento &lt;code&gt;Grafo de rutas&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Antes de usar el complemento hay que cargar la capa de líneas y luego configurar el plugin, mediante &lt;code&gt;Vectorial - Configuración del complemento Grafos de rutas&lt;/code&gt;. En ese apartado de configuración se puede fijar p.ej. la velocidad media de los tramos. Para ver los datos de tiempo y velocidad correctamente, será necesario usar una proyección que utilice metros como unidad, así que aplicaremos antes una reproyección al vuelo (en este caso, a la zona le corresponde ETRS89-UTM30N: EPSG:25830).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Si fijamos una velocidad media de 50 km/h a toda la red, podemos calcular por ejemplo la distancia y tiempo estimado del camino más corto entre la estación de tren de RENFE y los Jardines de Piquío en el Sardinero.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2014-12-18-como-utilizar-datos-geograficos-publicos-de-openstreetmap-con-un-software-libre-como-qgis/media/osm_redes.png&quot; alt=&quot;osm_redes.png&quot; /&gt;&lt;br /&gt;
Fig.3: Los datos de la red de transporte OSM utilizados para un cálculo de camino más corto&lt;/p&gt;
&lt;h2 id=&quot;ejemplo-3-usar-osm-para-la-busqueda-de-vivienda&quot; tabindex=&quot;-1&quot;&gt;Ejemplo 3. Usar OSM para la búsqueda de vivienda&lt;/h2&gt;
&lt;p&gt;Los datos OSM pueden usarse también para análisis de otra índole, encadenando sobre ellos operaciones espaciales sucesivas (área de influencia, intersección, etc.). P.ej. para la búsqueda de la mejor ubicación para alquilar una vivienda, en base a una serie de requisitos previos tales como p.ej. la proximidad a una escuela, cercanía a vías de comunicación rápidas, etc.&lt;/p&gt;
&lt;p&gt;En el siguiente vídeo puede verse un análisis vectorial sobre datos OSM en esta línea:&lt;br /&gt;
&lt;a href=&quot;https://vimeo.com/112687753&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;https://vimeo.com/112687753&lt;/a&gt;  &lt;/p&gt;
&lt;hr /&gt;
&lt;blockquote&gt;
&lt;p&gt;Esta entrada es una colaboración en el blog iNFoRMáTICa++, perteneciente a los Estudios de Informática, Multimedia y Telecomunicación (EIMT) de la Universitat Oberta de Catalunya (UOC). Publicada originalmente en: &lt;a href=&quot;http://informatica.blogs.uoc.edu/2014/12/15/como-utilizar-datos-geograficos-publicos-de-openstreetmap-con-un-software-libre-como-qgis/&quot; title=&quot;http://informatica.blogs.uoc.edu/2014/12/15/como-utilizar-datos-geograficos-publicos-de-openstreetmap-con-un-software-libre-como-qgis/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://informatica.blogs.uoc.edu/2014/12/15/como-utilizar-datos-geograficos-publicos-de-openstreetmap-con-un-software-libre-como-qgis/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2014-12-18-como-utilizar-datos-geograficos-publicos-de-openstreetmap-con-un-software-libre-como-qgis/como-utilizar-datos-geograficos-publicos-de-open-street-map-con-un-software-libre-como-qgis/"/>
  </entry><entry>
    <title>Instalación de librerías NetCDF + Python en un sistema Windows</title>
    <updated>2013-06-25T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2013-06-25-instalacion-de-librerias-netcdf-python-en-un-sistema-windows/instalacion-de-librerias-net-cdf-python-en-un-sistema-windows/</id>
    <content type="html">&lt;p&gt;La instalación adecuada de Python + NetCDF es algo muy extendido y documentado en sistemas Linux, pero no tanto en sistemas Windows. En esta entrada el objetivo es recoger los pasos necesarios para conseguirlo, a modo de referencia rápida con las instrucciones y enlaces oportunos.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2013-06-25-instalacion-de-librerias-netcdf-python-en-un-sistema-windows/media/python_nc.png&quot; alt=&quot;Python + netCDF&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.unidata.ucar.edu/software/netcdf/docs/faq.html&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;NetCDF&lt;/a&gt; es un formato binario de fichero (.nc) orientado a arrays, que facilita el manejo de varias dimensiones (x, y, z, tiempo...) de forma eficiente y flexible. Por esta razón está muy extendido en la comunidad científica, por ejemplo para guardar datos del medio como temperaturas, corrientes, viento, salinidad, etc. obtenidos mediante sensores o modelado. &lt;/p&gt;
&lt;p&gt;De forma indisoluble con el formato, existe un conjunto de librerías para NetCDF que facilitan el acceso a sus datos desde varios lenguajes (C, Fortran, Java, etc.) y que son la pasarela con la que trabaja el programador. Python es por su parte el lenguaje de scripting más potente y posee multitud de librerías para ampliar su campo de acción (para el manejo de datos SIG, cálculo intensivo, gráficos, etc.), por lo que juntos forman una buena combinación en el ámbito científico.&lt;/p&gt;
&lt;p&gt;Los pasos para configurarlos de forma integrada en una plataforma Windows de 32 bits son:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(A) Python 2.7.&lt;/strong&gt; Si aún no lo tenemos, la última versión disponible para la rama 2.x (la más extendida) es actualmente la 2.7.5, &lt;a href=&quot;http://www.python.org/ftp/python/2.7.5/python-2.7.5.msi&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;descargable aquí&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(B) NetCDF4.&lt;/strong&gt; Las librerías base están escritas en C y para usarlas es necesario descargarlas y compilarlas o, algo más práctico, obtener directamente los binarios. Siguiendo la segunda vía, &lt;a href=&quot;http://www.unidata.ucar.edu/netcdf/win_netcdf/netCDF4.3.0-NC4-32.exe&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;aquí está&lt;/a&gt; el instalador con la última versión para netCDF4. Para una descripción más detallada del procedimiento con estos binarios, ver &lt;a href=&quot;http://www.unidata.ucar.edu/software/netcdf/docs/winbin.html&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;este enlace&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(C) Acceso a las librerías NetCDF.&lt;/strong&gt; Para un enlace posterior a las librerías, es necesario agregar al PATH los siguientes directorios, generados por el instalador: &lt;em&gt;C:&#92;Program Files&#92;netCDF 4.3.0&#92;bin&lt;/em&gt; y &lt;em&gt;c:&#92;Program Files&#92;netCDF 4.3.0&#92;deps&#92;shared&#92;bin&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(D) Numpy.&lt;/strong&gt; El acceso a los datos de los ficheros NetCDF se realizará de forma efectiva con esta librería para Python, especializada en el manejo de arrays.  La última versión disponible hoy, la 1.7.1, para Python 2.7 está &lt;a href=&quot;https://pypi.python.org/packages/2.7/n/numpy/numpy-1.7.1.win32-py2.7.exe&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;disponible aquí&lt;/a&gt; &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(E) Interfaz de Python para netcdf4.&lt;/strong&gt; Ésta es la librería Python que permite leer y escribir en los netcdf desde scripts .py, actuando de pasarela hacia las librerías base previamente configuradas (C). El instalador para la 2.X está en &lt;a href=&quot;https://netcdf4-python.googlecode.com/files/netCDF4-1.0.4.win32-py2.7.exe&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;este enlace&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;(F) Validación.&lt;/strong&gt; Finalmente, para probar que todo está correcto, ejecutaremos un pequeño script de Python, leyendo un netcdf cualquiera (fichero &lt;a href=&quot;http://testnc.py/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;testNC.py&lt;/a&gt;). P.ej. estos &lt;a href=&quot;http://data.giss.nasa.gov/cgi-bin/gistemp/nmaps.cgi?year_last=2013&amp;amp;month_last=5&amp;amp;sat=4&amp;amp;sst=3&amp;amp;type=anoms&amp;amp;mean_gen=05&amp;amp;year1=2013&amp;amp;year2=2013&amp;amp;base1=1951&amp;amp;base2=1980&amp;amp;radius=1200&amp;amp;pol=reg&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;datos de la NASA&lt;/a&gt; muestran las anomalías térmicas sobre la superficie terrestre y es posible descargarlos en formato netCDF &lt;a href=&quot;http://data.giss.nasa.gov/cgi-bin/gistemp/nmaps_netcdf.cgi?id=GHCN_GISS_ERSST_1200km_Anom05_2013_2013_1951_1980&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;desde aquí&lt;/a&gt; en un fichero &amp;quot;&lt;a href=&quot;http://nmaps.nc/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;nmaps.nc&lt;/a&gt;&amp;quot;. Como nota útil, señalar que para visualizar de forma rápida el netcdf se puede usar un visor como &lt;a href=&quot;http://www.giss.nasa.gov/tools/panoply/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Panoply&lt;/a&gt; o un software SIG como gvSIG, que en su versión más reciente incluye soporte a &lt;a href=&quot;http://blog.gvsig.org/2013/04/30/datos-en-netcdf-para-gvsig-v2-0/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;este formato&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://testnc.py/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;testNC.py&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# -*- coding: utf-8 -*-
import netCDF4 as nc
import numpy as np

&#39;&#39;&#39;
Prueba de acceso a netCDF en Python Win32.
Abre un fichero .nc y obtiene el valor mínimo de una variable conocida (&#39;TEMPANOMALY&#39;)
&#39;&#39;&#39;

print(&#39;TEST netCDF en Python&#39;)

rutaFichero = &amp;quot;c:/users/admin/nmaps.nc&amp;quot;
fichero = nc.Dataset(rutaFichero)

print(&amp;quot;* Variables disponibles en el fichero:&amp;quot;)
for v in fichero.variables:
    print(v)

datos = fichero.variables[&amp;quot;TEMPANOMALY&amp;quot;][0]
print(&amp;quot;* Mayor anomalía de temperatura negativa: {0} K&amp;quot;.format(min(datos)))
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Nota: como los datos de la variable vienen en un array con &amp;quot;máscara&amp;quot; (para indicar dónde hay &#39;huecos&#39; sin datos), es posible utilizar las clases de numpy específicas y hacer algo como:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;minimo = np.ma.MaskedArray.min(fichero.variables[&amp;quot;TEMPANOMALY&amp;quot;][:])
maximo = np.ma.MaskedArray.max(fichero.variables[&amp;quot;TEMPANOMALY&amp;quot;][:])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Algunos otros &lt;a href=&quot;http://netcdf4-python.googlecode.com/svn/trunk/docs/netCDF4-module.html&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;ejemplos de uso de ficheros .nc en Python&lt;/a&gt;&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2013-06-25-instalacion-de-librerias-netcdf-python-en-un-sistema-windows/instalacion-de-librerias-net-cdf-python-en-un-sistema-windows/"/>
  </entry><entry>
    <title>Materiales curso OpenLayers e Ikimap - DGSIG 2013</title>
    <updated>2013-06-15T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2013-06-15-materiales-curso-openlayers-e-ikimap-dgsig-2013/materiales-curso-open-layers-e-ikimap-dgsig-2013/</id>
    <content type="html">&lt;h2 id=&quot;materiales-desarrollo-app-geograficas-web&quot; tabindex=&quot;-1&quot;&gt;Materiales desarrollo app geográficas web&lt;/h2&gt;
&lt;p&gt;En este post incluyo los materiales completos de la asignatura &amp;quot;Desarrollo de aplicaciones geográficas web&amp;quot;, que he estado impartiendo los últimos 3 años en el Curso de Experto en DGSIG de la Universidad de Cantabria. Son los utilizados en la última edición presencial del curso (enero de 2013) y tienen licencia &lt;a href=&quot;http://creativecommons.org/licenses/by-sa/3.0/&quot; title=&quot;Creative Commons&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Creative Commons&lt;/a&gt;. Creo que puede ser útil a personas que comienzan en este ámbito, como recurso para el aprendizaje autónomo.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2013-06-15-materiales-curso-openlayers-e-ikimap-dgsig-2013/media/portada-dgsig2013.png&quot; alt=&quot;DGSIG2013&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Se trata de una versión mejorada respecto a la que ya se publicó &lt;a href=&quot;http://victorvelarde.wordpress.com/2011/01/31/material-de-introduccion-a-openlayers/&quot; title=&quot;Material de introducción a OpenLayers&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;aquí&lt;/a&gt;  hace un tiempo, aportando una revisión general a los materiales de OpenLayers (versión 2.12), algunos nuevos contenidos sobre Ikimap y los ejercicios del curso. Se incluye:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 - Guía de la asignatura .- descripción de los objetivos y materiales&lt;/li&gt;
&lt;li&gt;1 - Materiales de apoyo.- contenidos teóricos básicos (neogeografía, programación web, plataformas de publicación y OpenLayers)&lt;/li&gt;
&lt;li&gt;2 - Presentación en el aula.- soporte visual de los contenidos.&lt;/li&gt;
&lt;li&gt;3 - Ejercicios.- carpeta con los enunciados y sus soluciones.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Los materiales se pueden descargar aquí: ![[dgsig_2013.zip]]&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;formacion-open-layers&quot; tabindex=&quot;-1&quot;&gt;Formación OpenLayers&lt;/h2&gt;
&lt;p&gt;Me preguntan de forma esporádica por correo electrónico sobre &lt;strong&gt;formación&lt;/strong&gt; en OpenLayers, generalmente por Internet. Al margen de la formación presencial, cursos en línea (en español) con contenidos de OpenLayers conozco en dos lugares (si alguien conoce más opciones, por favor que me lo diga y las incluyo):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Geospatialtraining-ES: &lt;a href=&quot;http://geospatialtraininges.com/pagina-1/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://geospatialtraininges.com/pagina-1/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;UNIGIS: &lt;a href=&quot;http://www.unigis.es/Oferta-Formativa/Cursos-Especializacion/Programacion-Aplicaciones-Web-Map-ce&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://www.unigis.es/Oferta-Formativa/Cursos-Especializacion/Programacion-Aplicaciones-Web-Map-ce&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;También existen una serie de materiales en abierto en varios lugares, útiles para auto-formación. Por ejemplo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://openlayers.bicimap.es/manualOpenLayers.html&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://openlayers.bicimap.es/manualOpenLayers.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.sonxurxo.com/2011/03/29/tutorial-de-openlayers-i-introduccion/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://blog.sonxurxo.com/2011/03/29/tutorial-de-openlayers-i-introduccion/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Y por supuesto, en inglés existen bastantes más recursos.&lt;/p&gt;
&lt;p&gt;Saludos!&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2013-06-15-materiales-curso-openlayers-e-ikimap-dgsig-2013/materiales-curso-open-layers-e-ikimap-dgsig-2013/"/>
  </entry><entry>
    <title>Nuevas tendencias los SIG en la nube</title>
    <updated>2012-10-13T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2012-10-13-sig-en-la-nube/nuevas-tendencias-los-sig-en-la-nube/</id>
    <content type="html">&lt;p&gt;Hoy se dispone de un abanico amplio de opciones para publicar información geográfica en Internet. Generalmente cuando un profesional se plantea esta tarea suele considerar alguna de estas &lt;strong&gt;vías&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Preparar un &lt;strong&gt;servidor propio con software privativo&lt;/strong&gt;, como el de la empresa ESRI (ArcGIS Server), de calidad alta pero modelos de licencia accesibles para pocos.&lt;/li&gt;
&lt;li&gt;Montarlo con &lt;strong&gt;software libre&lt;/strong&gt;, que ha ganado mucho en madurez y cuyo uso sigue extendiéndose, con productos como Geoserver y OpenLayers.&lt;/li&gt;
&lt;li&gt;No utilizar software SIG y delegar en &lt;strong&gt;servicios web de mapas externos y sus APIs&lt;/strong&gt; para programadores (como el archiconocido GoogleMaps).&lt;/li&gt;
&lt;li&gt;Configurar un &lt;strong&gt;entorno híbrido&lt;/strong&gt;, con elementos combinados de las opciones previas.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;En general, las dos primeras opciones funcionan bien en organizaciones con personal cualificado, capaz de crear y mantener entornos tecnológicos complejos y siempre que se haga una inversión decidida de recursos, lo cual no siempre es fácil (grandes empresas, administraciones... los clientes SIG tradicionales, vaya). La tercera vía es mucho más ágil y con menos barreras de entrada para pequeñas empresas, asociaciones, particulares... que cada vez lo usan más y para usos más variados (es lo que algunos llaman &amp;quot;neogeografía&amp;quot;).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2012-10-13-sig-en-la-nube/media/Nubes.jpg&quot; alt=&quot;Nubes.jpg&quot; /&gt;&lt;/p&gt;
&lt;iframe src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2012-10-13-sig-en-la-nube/media/sig-cloud-uoc.pdf&quot; width=&quot;100%&quot; height=&quot;900px&quot; title=&quot;sig-cloud-uoc.pdf&quot; style=&quot;border:1px solid #ccc;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;Sin embargo, hay más opciones a considerar... y es que en los últimos tiempos está emergiendo una tendencia denominada &lt;strong&gt;computación en la nube&lt;/strong&gt;. Ésta implica la aparición de hardware, plataformas y servicios web mediante los que se externaliza a empresas especializadas parte de las labores informáticas de organizaciones e individuos. Y dentro de estas plataformas existen ya varias que permiten el almacenamiento y publicación de información geográfica.&lt;/p&gt;
&lt;p&gt;Algunas de estas interesantes plataformas son: &lt;a href=&quot;http://www.ikimap.com/es&quot; title=&quot;IkiMap&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;IkiMap&lt;/a&gt;, &lt;a href=&quot;http://geocommons.com/&quot; title=&quot;GeoCommons&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;GeoCommons&lt;/a&gt; (recientemente adquirida por Esri), &lt;a href=&quot;https://sites.google.com/site/fusiontablestalks/stories&quot; title=&quot;Google Fusion Tables&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Google Fusion Tables&lt;/a&gt; o &lt;a href=&quot;http://cartodb.com/&quot; title=&quot;CartoDB&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;CartoDB&lt;/a&gt;. Se trata de espacios en la nube donde almacenar y publicar datos geográficos en forma de mapas, que además prestan especial atención a la facilidad de uso para el usuario, las redes sociales y el buen diseño. Además, son plataformas que generalmente incluyen un API para programadores, con lo que sus posibilidades de uso e integración con otras herramientas se amplían notablemente.&lt;/p&gt;
&lt;p&gt;Sin duda se trata de plataformas potencialmente utilizables por un número muy alto y variado de individuos y organizaciones, especialmente en los casos en los que las &amp;quot;vías tradicionales&amp;quot; no sean aplicables. Por ejemplo, un medio de información local podría fácilmente ubicar en un mapa las noticias que suceden en su municipio (p.ej. obras en marcha o planificadas, actos culturales, animales perdidos...), incrustar ese mapa en su blog y permitir la participación de los ciudadanos y la difusión de sus mapas en redes sociales.&lt;/p&gt;
&lt;p&gt;Por ejemplo, el siguiente &lt;a href=&quot;http://www.ikimap.com/es/map/visitas-blog-13-octubre-2012&quot; title=&quot;mapa&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;mapa&lt;/a&gt; ha sido creado en la plataforma &lt;strong&gt;Ikimap&lt;/strong&gt;, autodenominada la &amp;quot;red social de los mapas&amp;quot;. Crear un mapa como este es una labor rápida y sencilla. En páginas webs y blogs que lo admitan, también se puede embeber un mapa interactivo, a partir del código para crear un iframe que proporciona la plataforma&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2012-10-13-sig-en-la-nube/media/ikimap.jpg&quot; alt=&quot;ikimap.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Sin ninguna duda, en los proyectos de publicación de información geográfica, las plataformas SIG en la nube son una opción muy a tener en cuenta ...&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2012-10-13-sig-en-la-nube/nuevas-tendencias-los-sig-en-la-nube/"/>
  </entry><entry>
    <title>Aplicaciones geográficas web móviles</title>
    <updated>2011-12-04T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2011-12-04-aplicaciones-geograficas-web-moviles/aplicaciones-geograficas-web-moviles/</id>
    <content type="html">&lt;p&gt;Este es un tema que considero de gran interés y futuro: el desarrollo de aplicaciones web teniendo en mente los nuevos &lt;strong&gt;dispositivos móviles&lt;/strong&gt;, tales como smartphones y tablets (iphones, teléfonos android, ipads, etc.).&lt;/p&gt;
&lt;h2 id=&quot;presentacion-para-geoinquietos&quot; tabindex=&quot;-1&quot;&gt;Presentación para Geoinquietos&lt;/h2&gt;
&lt;p&gt;Recientemente hablamos de este tema en &lt;strong&gt;&lt;a href=&quot;https://wiki.osgeo.org/wiki/Geoinquietos_Cantabria&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;geoinquietos Cantabria&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Esta fue la presentación que compartí con ellos:&lt;br /&gt;
&lt;strong&gt;&lt;a href=&quot;http://www.slideshare.net/VictorVelarde/geoinquietos-la-web-movil-est-aqu&quot; title=&quot;Geoinquietos: La web movil está aquí...&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Geoinquietos: La web movil está aquí...&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2011-12-04-aplicaciones-geograficas-web-moviles/media/portada-geoinquietos-webmovil.png&quot; alt=&quot;portada-geoinquietos-webmovil.png&quot; /&gt;&lt;/p&gt;
&lt;iframe src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2011-12-04-aplicaciones-geograficas-web-moviles/media/geoinquietos-webmovil.pdf&quot; width=&quot;100%&quot; height=&quot;900px&quot; title=&quot;geoinquietos-webmovil.pdf&quot; style=&quot;border:1px solid #ccc;&quot;&gt;&lt;/iframe&gt;
&lt;blockquote&gt;
&lt;p&gt;Si no conoces la iniciativa geoinquietos, infórmate en &lt;strong&gt;&lt;a href=&quot;http://wiki.osgeo.org/wiki/Cap%C3%ADtulo_Local_de_la_comunidad_hispano-hablante&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;osgeo&lt;/a&gt;&lt;/strong&gt; y busca tu grupo más cercano.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;herramientas-de-desarrollo&quot; tabindex=&quot;-1&quot;&gt;Herramientas de desarrollo&lt;/h2&gt;
&lt;p&gt;Frente al desarrollo en entornos nativos (p.ej. con Java y eclipse para android), la programación web estándar (HTML, Javascript y CSS + un lenguaje de servidor como ASP o PHP) puede resultar una opción muy interesante.&lt;/p&gt;
&lt;p&gt;Sobre la base de estas herramientas habituales de programación web están apareciendo varios complementos para el desarrollo móvil, sobre todo en forma de librerías javascript. De ellas, las dos que me parecen más reseñables son &lt;strong&gt;&lt;a href=&quot;http://www.sencha.com/products/touch&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Sencha Touch&lt;/a&gt;&lt;/strong&gt; [1] y &lt;strong&gt;&lt;a href=&quot;http://jquerymobile.com/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;jQuery Mobile&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Personalmente, dada la expansión de su hermana mayor jQuery, creo que la segunda es una opción que merece la pena probar.&lt;/p&gt;
&lt;p&gt;Las capacidades de su API pueden estudiarse en la página oficial y varios ejemplos reales de su uso se pueden ver en la &lt;strong&gt;&lt;a href=&quot;http://www.jqmgallery.com/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;jqmgallery&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Además, existe una buena capacidad de integración entre jquery mobile y OpenLayers en su última versión, como puede verse p.ej. en este &lt;strong&gt;&lt;a href=&quot;http://openlayers.org/dev/examples/mobile-jq.html#mappage&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;enlace&lt;/a&gt;&lt;/strong&gt;. Esta es una gran combinación para realizar aplicaciones geográficas web móviles.&lt;/p&gt;
&lt;p&gt;Saludos a todos.&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2011-12-04-aplicaciones-geograficas-web-moviles/aplicaciones-geograficas-web-moviles/"/>
  </entry><entry>
    <title>OpenLayers y pestañas con jQuery</title>
    <updated>2011-04-21T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2011-04-21-openlayers-y-pestanas-de-jquery/open-layers-y-pestanas-con-j-query/</id>
    <content type="html">&lt;p&gt;En este artículo vamos a ver cómo incluir pestañas de jquery (&lt;em&gt;jqueryui tabs&lt;/em&gt;) dentro de un popup de OpenLayers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2011-04-21-openlayers-y-pestanas-de-jquery/media/jquerytabsenopenlayers.png&quot; alt=&quot;jquerytabsenopenlayers.png&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;j-query&quot; tabindex=&quot;-1&quot;&gt;jQuery&lt;/h2&gt;
&lt;p&gt; &lt;a href=&quot;http://jquery.com/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;jQuery&lt;/a&gt;  se ha convertido en la librería por antonomasia para extender el javascript estándar en las páginas web modernas. Si desarrollas para la web, tienes que conocerla. Pero... &lt;em&gt;¿cómo trabajar con jQuery + OpenLayers?&lt;/em&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hay algunos intentos de desarrollar un plugin para jQuery que permita trabajar con OpenLayers, como &lt;a href=&quot;http://blog.spaziogis.it/static/jquerymap/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;MapQuery&lt;/a&gt;, que antes se denominaba GeoJQuery. El proyecto lleva poco tiempo, pero habrá que seguir sus pasos; quizá pronto sea una alternativa a &lt;a href=&quot;http://www.geoext.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;GeoExt&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Mientras tanto es más frecuente que tengamos en marcha un proyecto con OpenLayers y queramos utilizar puntualmente jQuery, especialmente algún componente visual o widget de &lt;strong&gt;jqueryui&lt;/strong&gt;, como las pestañas (&lt;em&gt;tabs&lt;/em&gt;) o un acordeón (&lt;a href=&quot;http://jqueryui.com/demos/tabs/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;accordion&lt;/a&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Vamos a ver cómo hacer en el caso 2, con un ejemplo sencillo.&lt;/p&gt;
&lt;h2 id=&quot;codigo-completo&quot; tabindex=&quot;-1&quot;&gt;Código completo&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;es&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;
    &amp;lt;meta name=&amp;quot;viewport&amp;quot; content=&amp;quot;width=device-width, initial-scale=1.0&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Pestañas de jQuery en OpenLayers&amp;lt;/title&amp;gt;
    
    &amp;lt;!-- Estilos de jQuery UI --&amp;gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css&amp;quot;&amp;gt;
    
    &amp;lt;!-- Librerías necesarias --&amp;gt;
    &amp;lt;script src=&amp;quot;https://code.jquery.com/jquery-3.6.0.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;https://code.jquery.com/ui/1.12.1/jquery-ui.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&amp;quot;https://openlayers.org/api/OpenLayers.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
    
    &amp;lt;style&amp;gt;
        &lt;a class=&quot;tag&quot; data-content=&quot;#mapa&quot;&gt;#mapa&lt;/a&gt; {
            width: 512px;
            height: 512px;
            border: 1px solid &lt;a class=&quot;tag&quot; data-content=&quot;#ccc&quot;&gt;#ccc&lt;/a&gt;;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div id=&amp;quot;mapa&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;script&amp;gt;
        // Función para preparar el contenido del popup con pestañas
        function prepararFicha(ciudad) {
            var div = $(&amp;quot;&amp;lt;div&amp;gt;&amp;quot;);
            var contenido = $(&amp;quot;&amp;lt;div id=&#39;popupConTabs&#39; style=&#39;font-size: 9px;&#39;&amp;gt;&amp;quot;);

            var titulos = $(&amp;quot;&amp;lt;ul&amp;gt;&amp;quot;)
                .append(&amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&#39;#tab-1&#39;&amp;gt;Atributos&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;quot;)
                .append(&amp;quot;&amp;lt;li&amp;gt;&amp;lt;a href=&#39;#tab-2&#39;&amp;gt;Wikipedia&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;quot;);

            contenido.append(titulos);

            // Pestaña 1 - Información de la ciudad
            var p1 = $(&amp;quot;&amp;lt;div id=&#39;tab-1&#39;&amp;gt;&amp;quot;).append(
                $(&amp;quot;&amp;lt;p&amp;gt;&amp;quot;).html(
                    &amp;quot;&amp;lt;b&amp;gt;Nombre:&amp;lt;/b&amp;gt; &amp;quot; + ciudad.attributes.Nombre + &amp;quot;&amp;lt;br/&amp;gt;&amp;quot; +
                    &amp;quot;&amp;lt;b&amp;gt;Habs.:&amp;lt;/b&amp;gt; &amp;quot; + ciudad.attributes.Habitantes
                )
            );
            contenido.append(p1);

            // Pestaña 2 - Enlace a Wikipedia
            var p2 = $(&amp;quot;&amp;lt;div id=&#39;tab-2&#39;&amp;gt;&amp;quot;).append(
                $(&amp;quot;&amp;lt;p&amp;gt;&amp;quot;).html(&amp;quot;&amp;lt;a href=&#39;&amp;quot; + ciudad.attributes.Wikipedia + &amp;quot;&#39; target=&#39;_blank&#39;&amp;gt;Wikipedia&amp;lt;/a&amp;gt;&amp;quot;)
            );
            contenido.append(p2);

            div.append(contenido);
            return div.html();
        }

        // Definiciones de proyecciones
        var WGS84 = new OpenLayers.Projection(&amp;quot;EPSG:4326&amp;quot;);
        var MERCATOR = new OpenLayers.Projection(&amp;quot;EPSG:900913&amp;quot;);

        // Definir el encuadre inicial del mapa
        var peninsula = new OpenLayers.Bounds(-18.0, 26.0, 6.0, 44.0).transform(WGS84, MERCATOR);
        var mapa = new OpenLayers.Map(&amp;quot;mapa&amp;quot;);
        mapa.addLayer(new OpenLayers.Layer.OSM());
        mapa.zoomToExtent(peninsula);

        // Crear una capa vectorial y añadir una ciudad
        var ciudades = new OpenLayers.Layer.Vector(&amp;quot;Ciudades&amp;quot;);
        mapa.addLayer(ciudades);

        var geometria = new OpenLayers.Geometry.Point(-3.8, 43.46).transform(WGS84, MERCATOR);
        var atributos = {
            Nombre: &amp;quot;Santander&amp;quot;,
            Habitantes: 181589,
            Wikipedia: &amp;quot;http://es.wikipedia.org/wiki/Santander_(Espa%C3%B1a)&amp;quot;
        };

        var santander = new OpenLayers.Feature.Vector(geometria, atributos);
        ciudades.addFeatures([santander]);

        // Control de selección de elementos en el mapa
        var controlSeleccion = new OpenLayers.Control.SelectFeature(ciudades);
        mapa.addControl(controlSeleccion);
        controlSeleccion.activate();

        // Variables para los popups
        var popup;

        // Evento cuando se selecciona una ciudad
        ciudades.events.on({
            featureselected: function (e) {
                var ciudad = e.feature;
                var html = prepararFicha(ciudad);

                popup = new OpenLayers.Popup.FramedCloud(
                    &amp;quot;Info ciudad&amp;quot;,
                    ciudad.geometry.getBounds().getCenterLonLat(),
                    null,
                    html,
                    null,
                    true,
                    function () {
                        controlSeleccion.unselect(ciudad);
                    }
                );

                popup.minSize = new OpenLayers.Size(300, 100);
                ciudad.popup = popup;
                mapa.addPopup(popup);

                // Activación de las pestañas de jQuery UI
                $(&amp;quot;#popupConTabs&amp;quot;).tabs();
            },
            featureunselected: function (e) {
                var ciudad = e.feature;
                if (ciudad.popup) {
                    mapa.removePopup(ciudad.popup);
                    ciudad.popup.destroy();
                    ciudad.popup = null;
                }
            }
        });
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;http://jsfiddle.net/VictorVelarde/DbCgt/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://jsfiddle.net/VictorVelarde/DbCgt/&lt;/a&gt;&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2011-04-21-openlayers-y-pestanas-de-jquery/open-layers-y-pestanas-con-j-query/"/>
  </entry><entry>
    <title>Menu contextual sobre mapa - API Javascript ESRI</title>
    <updated>2011-02-08T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2011-02-08-menu-contextual-mapa-api-javascript-arcgis-server/menu-contextual-sobre-mapa-api-javascript-esri/</id>
    <content type="html">&lt;p&gt;Lo siguiente es una pequeña guía de algo que me parece muy útil: generar un menu contextual asociado al botón derecho cuando estás sobre el mapa. Al margen de tener la típica barra superior de herramientas, con este tipo de menú podremos tener más a mano cualquier acción invocable desde javascript que nos interese, como p.ej. hacer zoom más en un punto o recoger las coordenadas del clic y usarlas para otra acción como p.ej. un geocoding.&lt;/p&gt;
&lt;p&gt;El resultado final sería algo como esto:&lt;br /&gt;
&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2011-02-08-menu-contextual-mapa-api-javascript-arcgis-server/media/menumapa.png&quot; alt=&quot;menumapa.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Para el menu vamos a usar la librería de Javascript dojo y el mapa estará realizado con el &lt;a href=&quot;http://help.arcgis.com/EN/webapi/javascript/arcgis/index.html&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;API ESRI de Javascript&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Tomaremos como base un &lt;a href=&quot;https://www.victorvelarde.com/(http://resources.esri.com/help/9.3/arcgisserver/apis/javascript/arcgis/demos/map/map_topo.html)&quot; class=&quot;internal-link&quot;&gt;ejemplo de ESRI para mostrar un mapa topográfico&lt;/a&gt;. Y ahora a ese codigo vamos a añadirle la funcionalidad del menu emergente.&lt;/p&gt;
&lt;p&gt;Para ello:&lt;/p&gt;
&lt;h2 id=&quot;a-importamos-el-control-de-dojo-para-el-menu&quot; tabindex=&quot;-1&quot;&gt;a) Importamos el control de dojo para el menu.&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;dojo.require(&amp;quot;dijit.Menu&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;b-anadimos-el-marcado-en-el-html&quot; tabindex=&quot;-1&quot;&gt;b) Añadimos el marcado en el HTML&lt;/h2&gt;
&lt;p&gt;Añadimos al HTML de la pagina el código de marcado del diálogo (también se podría crear al vuelo con javascript). Los atributos son importantes para vincularlo correctamente al mapa y cada entrada del menu tiene asociada la función de javascript que queramos:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div dojotype=&amp;quot;dijit.Menu&amp;quot; id=&amp;quot;menu&amp;quot; contextmenuforwindow=&amp;quot;false&amp;quot; 
     style=&amp;quot;display: none;&amp;quot; targetnodeids=&amp;quot;map&amp;quot;&amp;gt;
    &amp;lt;div dojotype=&amp;quot;dijit.MenuItem&amp;quot; onclick=&amp;quot;Foo();&amp;quot;&amp;gt;Foo&amp;lt;/div&amp;gt;
    &amp;lt;div dojotype=&amp;quot;dijit.MenuSeparator&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;div dojotype=&amp;quot;dijit.MenuItem&amp;quot; onclick=&amp;quot;Bar();&amp;quot;&amp;gt;Bar&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;c-captura-del-evento-de-clic-derecho&quot; tabindex=&quot;-1&quot;&gt;c) Captura del evento de clic derecho&lt;/h2&gt;
&lt;p&gt;Y finalmente la captura del evento &#39;clic botón derecho&#39; y el guardado del punto del mapa. De manera automática y justo a continuación de la gestion del evento, dojo se encargará de mostrar el menu emergente y ya podremos usar en nuestras funciones el último valor de la variable &lt;em&gt;lastPoint&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;dojo.connect(map, &#39;onMouseDown&#39;, this, function(evt) {
    if (evt.button !== 2) return; // solo botón derecho
    lastPoint = evt.mapPoint;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;d-codigo-completo&quot; tabindex=&quot;-1&quot;&gt;d) Código completo&lt;/h2&gt;
&lt;p&gt;El código completo sería algo como:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&amp;quot;utf-8&amp;quot;&amp;gt;
    &amp;lt;title&amp;gt;Topographic Map&amp;lt;/title&amp;gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; 
          href=&amp;quot;http://serverapi.arcgisonline.com/jsapi/arcgis/1.6/js/dojo/dijit/themes/tundra/tundra.css&amp;quot;&amp;gt;
    
    &amp;lt;style&amp;gt;
        html, body {
            height: 100%;
            width: 100%;
            margin: 0;
            padding: 0;
        }
    &amp;lt;/style&amp;gt;

    &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
        var djConfig = { parseOnLoad: true };
    &amp;lt;/script&amp;gt;

    &amp;lt;script type=&amp;quot;text/javascript&amp;quot; 
            src=&amp;quot;http://serverapi.arcgisonline.com/jsapi/arcgis/?v=1.6&amp;quot;&amp;gt;
    &amp;lt;/script&amp;gt;

    &amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;
        dojo.require(&amp;quot;dijit.layout.BorderContainer&amp;quot;);
        dojo.require(&amp;quot;dijit.layout.ContentPane&amp;quot;);
        dojo.require(&amp;quot;esri.map&amp;quot;);
        dojo.require(&amp;quot;dijit.Menu&amp;quot;);

        var map;
        var lastPoint;

        function Foo() {
            alert(&amp;quot;x: &amp;quot; + lastPoint.x + &amp;quot; y: &amp;quot; + lastPoint.y);
        }

        function Bar() {
            alert(&#39;Bar&#39;);
        }

        function init() {
            var initExtent = new esri.geometry.Extent({
                &amp;quot;xmin&amp;quot;: -13635568,
                &amp;quot;ymin&amp;quot;: 4541606,
                &amp;quot;xmax&amp;quot;: -13625430,
                &amp;quot;ymax&amp;quot;: 4547310,
                &amp;quot;spatialReference&amp;quot;: { &amp;quot;wkid&amp;quot;: 102100 }
            });

            map = new esri.Map(&amp;quot;map&amp;quot;, { extent: initExtent });

            var basemap = new esri.layers.ArcGISTiledMapServiceLayer(
                &amp;quot;http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer&amp;quot;
            );

            map.addLayer(basemap);

            var resizeTimer;

            dojo.connect(map, &#39;onLoad&#39;, function (theMap) {
                dojo.connect(dijit.byId(&#39;map&#39;), &#39;resize&#39;, function () {
                    clearTimeout(resizeTimer);
                    resizeTimer = setTimeout(function () {
                        map.resize();
                        map.reposition();
                    }, 500);
                });
            });

            dojo.connect(map, &#39;onMouseDown&#39;, this, function (evt) {
                if (evt.button !== 2) return; // Only right button
                lastPoint = evt.mapPoint;
            });
        }

        dojo.addOnLoad(init);
    &amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body class=&amp;quot;tundra&amp;quot;&amp;gt;
    &amp;lt;div dojotype=&amp;quot;dijit.layout.BorderContainer&amp;quot; 
         design=&amp;quot;headline&amp;quot; 
         gutters=&amp;quot;false&amp;quot; 
         style=&amp;quot;width: 100%; height: 100%; margin: 0;&amp;quot;&amp;gt;
        
        &amp;lt;div id=&amp;quot;map&amp;quot; 
             dojotype=&amp;quot;dijit.layout.ContentPane&amp;quot; 
             region=&amp;quot;center&amp;quot; 
             style=&amp;quot;overflow: hidden;&amp;quot;&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;!-- Right-button menu --&amp;gt;
    &amp;lt;div dojotype=&amp;quot;dijit.Menu&amp;quot; id=&amp;quot;menu&amp;quot; 
         contextmenuforwindow=&amp;quot;false&amp;quot; 
         style=&amp;quot;display: none;&amp;quot; 
         targetnodeids=&amp;quot;map&amp;quot;&amp;gt;
        
        &amp;lt;div dojotype=&amp;quot;dijit.MenuItem&amp;quot; onclick=&amp;quot;Foo();&amp;quot;&amp;gt;Foo&amp;lt;/div&amp;gt;
        &amp;lt;div dojotype=&amp;quot;dijit.MenuSeparator&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;div dojotype=&amp;quot;dijit.MenuItem&amp;quot; onclick=&amp;quot;Bar();&amp;quot;&amp;gt;Bar&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La última versión del API para JS disponible con la que lo hemos probado es la v2.1 pero no debería ser un problema usar el código en otras versiones.&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2011-02-08-menu-contextual-mapa-api-javascript-arcgis-server/menu-contextual-sobre-mapa-api-javascript-esri/"/>
  </entry><entry>
    <title>Material de introducción a OpenLayers</title>
    <updated>2011-01-31T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2011-01-31-material-de-introduccion-a-openlayers/material-de-introduccion-a-open-layers/</id>
    <content type="html">&lt;p&gt;Recientemente he tenido el placer de participar como profesor dentro de la primera edición del curso &lt;a href=&quot;http://www.unican.es/NR/rdonlyres/9F06DA8F-9929-4FE5-8D92-E78EFA15518A/57934/TRIPTICO_CURSOEXPERTODGSIG_2010A2011.pdf&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Experto en Desarrollo y Gestión de Sistemas de Información Geográfica&lt;/a&gt; de la Universidad de Cantabria.&lt;/p&gt;
&lt;p&gt;El módulo que impartí era una introducción rápida a &lt;strong&gt;OpenLayers&lt;/strong&gt;. La mayoría de los alumnos eran geógrafos, pero también había biólogos, historiadores, sociólogos... sin una base previa de programación pero con interés en aprender.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2011-01-31-material-de-introduccion-a-openlayers/media/openlayers.png&quot; alt=&quot;openlayers.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Cuelgo a continuación el documento de apuntes que se les entregó a los alumnos para apoyar y complementar los ejercicios prácticos hechos en clase.&lt;/p&gt;
&lt;iframe src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2011-01-31-material-de-introduccion-a-openlayers/media/curso-openlayers-victorvelarde-2011.pdf&quot; width=&quot;100%&quot; height=&quot;900px&quot; title=&quot;curso-openlayers-victorvelarde-2011.pdf&quot; style=&quot;border:1px solid #ccc;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;El documento pretende recoger los fundamentos básicos útiles (sobre neogeografía, mashups, html, javascript...) para que una persona sin conocimientos previos de programación se sienta cómoda al empezar a trabajar con OpenLayers.&lt;/p&gt;
&lt;p&gt;Saludos y que sea útil.&lt;/p&gt;
&lt;p&gt;Nota (15-06-2013): Estos materiales han sido revisados y ampliados, ver &lt;a class=&quot;internal-link&quot; target=&quot;&quot; data-note-icon=&quot;&quot; href=&quot;https://www.victorvelarde.com/articulos/2013-06-15-materiales-curso-openlayers-e-ikimap-dgsig-2013/materiales-curso-open-layers-e-ikimap-dgsig-2013/&quot;&gt;Materiales curso OpenLayers e Ikimap - DGSIG 2013&lt;/a&gt;&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2011-01-31-material-de-introduccion-a-openlayers/material-de-introduccion-a-open-layers/"/>
  </entry><entry>
    <title>Convertir rasters de un directorio a otro formato con gdal</title>
    <updated>2010-05-10T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2010-05-10-convertir-rasters-de-un-directorio-a-otro-formato-con-gdal/convertir-rasters-de-un-directorio-a-otro-formato-con-gdal/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2010-05-10-convertir-rasters-de-un-directorio-a-otro-formato-con-gdal/media/gdal.png&quot; alt=&quot;gdal.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Teniendo acceso desde línea de comandos a la utilidad &lt;strong&gt;gdal_translate&lt;/strong&gt; (distribuida p.ej. en FWTools), podemos convertir todos los ficheros .ecw de una carpeta a formato .tif, mediante el siguiente código en Python:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import os

directorio_actual = os.getcwd()
directorio_salida = os.path.join(directorio_actual, &amp;quot;resultados&amp;quot;)

os.makedirs(directorio_salida, exist_ok=True)

for root, dirs, files in os.walk(directorio_actual):
    for fichero in files:
        nombre_fichero, extension = os.path.splitext(fichero)
        if extension == &amp;quot;.ecw&amp;quot;:
            comando = f&amp;quot;gdal_translate {fichero} {os.path.join(directorio_salida, nombre_fichero + &#39;.tif&#39;)}&amp;quot;
            print(comando)
            os.system(comando)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copiamos el código a un fichero llamado p.ej. &lt;code&gt;ejecutar.py&lt;/code&gt;dentro de la carpeta que nos interese, creamos una carpeta dentro para los ficheros transformados (&#39;resultados&#39;) y ejecutamos... Más fácil imposible.&lt;/p&gt;
&lt;p&gt;Se pueden introducir fácilmente otras variantes, p.ej. con otros formatos de raster, viendo las opciones disponibles en la herramienta &lt;a href=&quot;http://www.gdal.org/gdal_translate.html&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;gdal_translate&lt;/a&gt;&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2010-05-10-convertir-rasters-de-un-directorio-a-otro-formato-con-gdal/convertir-rasters-de-un-directorio-a-otro-formato-con-gdal/"/>
  </entry><entry>
    <title>Geoproceso interactivo vs geoproceso en batch</title>
    <updated>2009-12-29T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2009-12-29-geoproceso-interactivo-geoproceso-en-batch/geoproceso-interactivo-vs-geoproceso-en-batch/</id>
    <content type="html">&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2009-12-29-geoproceso-interactivo-geoproceso-en-batch/media/esrigp.gif&quot; alt=&quot;esrigp.gif&quot; /&gt;&lt;/p&gt;
&lt;p&gt;En ocasiones, con geoprocesos que tienen como parámetro de entrada entidades vectoriales, es interesante que podamos ejecutarlos de dos modos:&lt;/p&gt;
&lt;p&gt;(1) de manera interactiva (&lt;strong&gt;dibujando&lt;/strong&gt; nosotros las geometrías) o (2) suministrando una &lt;strong&gt;capa ya existente&lt;/strong&gt; que tenga las entidades  (p.ej. un shapefile). Dentro del entorno de geoprocesos de ESRI, esto se corresponde con la definición de un parámetro de tipo &lt;strong&gt;FeatureSet&lt;/strong&gt; (1) y un parámetro como &lt;strong&gt;FeatureLayer&lt;/strong&gt; (2), respectivamente.&lt;/p&gt;
&lt;p&gt;[Esto en la versión 9.3 de ArcGIS Desktop viene incorporado &#39;de serie&#39;, así que no hay problema. Lo que se comenta a continuación en este post es aplicable solo para la versión &lt;strong&gt;9.2&lt;/strong&gt;]&lt;/p&gt;
&lt;p&gt;Una manera sencilla de que el mismo script pueda trabajar con los dos modos es como se muestra a continuación:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;gp = arcgisscripting.create()
gp.overwriteoutput = 1
if gp.ScratchWorkspace is None:
    gp.ScratchWorkspace = gp.GetSystemEnvironment(&amp;quot;TEMP&amp;quot;)
# Parametros de entrada.........
fs = gp.GetParameter(0)   # (1) FeatureSet [opcional]
fLayer = gp.GetParameter(1)   #(2) FeatureLayer [opcional]
# Detección del MODO DE USO
if (str(fLayer).strip() != &amp;quot;&amp;quot;):
    gp.AddMessage(&amp;quot;Trabajando con modo batch...&amp;quot;)
else:
    gp.AddMessage(&amp;quot;Trabajando con modo interactivo...&amp;quot;)
    &lt;a class=&quot;tag&quot; data-content=&quot;#Adaptación&quot;&gt;#Adaptación&lt;/a&gt; del FeatureSet a Feature Layer
    tmp = str(int(math.ceil(random.random()*1000000)))
    fLayer = &amp;quot;%s&#92;&#92;%s.shp&amp;quot; %(gp.ScratchWorkspace, tmp)
    fs.Save(fLayer)
# A partir de aquí, el tratamiento puede ser igual
registros = gp.SearchCursor(fLayer)
registro = registros.Next()
while registro:
    # tratamiento del registro [...]
    registro = registros.Next()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En la línea tmp = ... también puede usarse &lt;code&gt;gp.CreateScratchName&lt;/code&gt;&lt;br /&gt;
Que sea útil.&lt;/p&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2009-12-29-geoproceso-interactivo-geoproceso-en-batch/geoproceso-interactivo-vs-geoproceso-en-batch/"/>
  </entry><entry>
    <title>Descarga de ficheros desde Web Coverage Service (WCS)</title>
    <updated>2009-11-09T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2009-11-09-descarga-de-ficheros-desde-web-coverage-service-wcs/descarga-de-ficheros-desde-web-coverage-service-wcs/</id>
    <content type="html">&lt;p&gt;Este script sirve para realizar peticiones a un servicio OGC-WCS para, dado un encuadre y una &#39;coverage&#39; conocida, descargar su información a local en ficheros. Está realizado con python y utiliza la librería urllib2. Los ficheros raster generados, en Geotiff o AsciiGrid, pueden luego cargarse en cualquier programa GIS o manipularse con librerías estándar como gdal.&lt;/p&gt;
&lt;p&gt;El resultado en este caso es un modelo digital del terreno como éste:&lt;br /&gt;
&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2009-11-09-descarga-de-ficheros-desde-web-coverage-service-wcs/media/dem2d.jpg&quot; alt=&quot;dem2d.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;http://utilidadeswcs.py/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;UtilidadesWCS.py&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import os
import time
import urllib2

class ServicioWCS:
    &amp;quot;&amp;quot;&amp;quot;
    Utilidad para generar peticiones a un servicio OGC-WCS y guardar los resultados en disco.
    
    @author: VictorVelarde (victor.velarde@gmail.com)
    &amp;quot;&amp;quot;&amp;quot;
    
    # Class constants (in same units as projection system)
    ANCHURA_PETICION = 10000
    SEGUNDOS_ESPERA_ENTRE_PETICIONES = 5
    
    def __init__(self, url):
        &amp;quot;&amp;quot;&amp;quot;
        Constructor with service URL.
        
        Args:
            url (str): Base URL for the WCS service, 
                      e.g.: http://www.idee.es/wcs/IDEE-WCS-UTM30N/wcsServlet?
        &amp;quot;&amp;quot;&amp;quot;
        self.url = url
    
    def ExtraerFicheros(self, xmin, ymin, xmax, ymax, directorioSalida, formatoWCS):
        &amp;quot;&amp;quot;&amp;quot;
        Extract files from WCS service based on given coordinates.
        
        Args:
            xmin (float): Minimum X coordinate
            ymin (float): Minimum Y coordinate
            xmax (float): Maximum X coordinate
            ymax (float): Maximum Y coordinate
            directorioSalida (str): Output directory path
            formatoWCS (str): WCS format type
        &amp;quot;&amp;quot;&amp;quot;
        if not os.path.isdir(directorioSalida):
            os.mkdir(directorioSalida)
            
        print(&#39;Inicio de la extraccion&#39;)
        i = 1
        
        for x in range(xmin, xmax, ServicioWCS.ANCHURA_PETICION):
            for y in range(ymin, ymax, ServicioWCS.ANCHURA_PETICION):
                url = (
                    f&#39;{self.url}?REQUEST=GetCoverage&amp;amp;SERVICE=WCS&amp;amp;VERSION=1.0.0&#39;
                    f&#39;&amp;amp;FORMAT={formatoWCS}&amp;amp;COVERAGE=MDT25_peninsula_zip&#39;
                    f&#39;&amp;amp;BBOX={x},{y},{x + ServicioWCS.ANCHURA_PETICION},&#39;
                    f&#39;{y + ServicioWCS.ANCHURA_PETICION}&amp;amp;CRS=EPSG:23030&#39;
                    f&#39;&amp;amp;RESX=25&amp;amp;RESY=25&#39;
                )
                
                print(url)
                peticion = urllib2.Request(url)
                respuesta = urllib2.urlopen(peticion)
                
                extension = {
                    FormatoWCS.ASCII_GRID: &#39;.asc&#39;,
                    FormatoWCS.GEOTIFF: &#39;.tif&#39;
                }
                
                with open(f&#39;{directorioSalida}/{i:03d}{extension[formatoWCS]}&#39;, &#39;wb&#39;) as fileHandle:
                    fileHandle.write(respuesta.read())
                
                time.sleep(ServicioWCS.SEGUNDOS_ESPERA_ENTRE_PETICIONES)
                i += 1
                
        print(&#39;Fin de la extraccion&#39;)


class FormatoWCS:
    &amp;quot;&amp;quot;&amp;quot;Available WCS format types.&amp;quot;&amp;quot;&amp;quot;
    ASCII_GRID = &#39;AsciiGrid&#39;
    GEOTIFF = &#39;Geotiff&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y para ejecutarlo:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from UtilidadesWCS import ServicioWCS, FormatoWCS

# Initialize WCS service with IDEE&#39;s URL
servicio = ServicioWCS(&amp;quot;http://www.idee.es/wcs/IDEE-WCS-UTM30N/wcsServlet&amp;quot;)

# Extract data in ASCII Grid format
servicio.ExtraerFicheros(
    xmin=333850.0,
    ymin=4728100.0,
    xmax=503850.0,
    ymax=4888100.0,
    directorioSalida=&amp;quot;extraccion&amp;quot;,
    formatoWCS=FormatoWCS.ASCII_GRID
)

# Extract same area in GeoTIFF format
servicio.ExtraerFicheros(
    xmin=333850.0,
    ymin=4728100.0,
    xmax=503850.0,
    ymax=4888100.0,
    directorioSalida=&amp;quot;extraccion&amp;quot;,
    formatoWCS=FormatoWCS.GEOTIFF
)
&lt;/code&gt;&lt;/pre&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2009-11-09-descarga-de-ficheros-desde-web-coverage-service-wcs/descarga-de-ficheros-desde-web-coverage-service-wcs/"/>
  </entry><entry>
    <title>Conferencia de usuarios ESRI España 2009</title>
    <updated>2009-10-03T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2009-10-03-conferenciaesri2009/conferencia-de-usuarios-esri-espana-2009/</id>
    <content type="html">&lt;p&gt;El pasado día 30 de septiembre y 1 de octubre se celebró en el IFEMA de Madrid la conferencia de usuarios de ESRI ESpaña 2009. Aunque hubo ponencias temáticas sobre aspectos variados como utilities, administración local o defensa, acudí sobre todo a ponencias técnicas.&lt;/p&gt;
&lt;h2 id=&quot;geolics&quot; tabindex=&quot;-1&quot;&gt;Geolics&lt;/h2&gt;
&lt;p&gt;Como resultado de los trabajos que hemos estado realizando con ArcGIS Server y geoprocesos en el IHCantabria, presenté el poster Geolics.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.victorvelarde.com/img/user/Blog/Articulos/2009-10-03-conferenciaesri2009/media/presion-contaminacion-atmosferica.png&quot; alt=&quot;presion-contaminacion-atmosferica.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.victorvelarde.com/img/docs/poster-geolics.pdf&quot; class=&quot;internal-link&quot;&gt;poster-geolics.pdf&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;conferencia&quot; tabindex=&quot;-1&quot;&gt;Conferencia&lt;/h2&gt;
&lt;p&gt;En la conferencia se presentaron algunas cuestiones interesantes, sobre la versión 9.3.1 y algunos avances de la próxima v9.4 (estimada para mayo de 2010). Algunas cosas que considero interesantes:&lt;/p&gt;
&lt;h3 id=&quot;arc-gis-desktop&quot; tabindex=&quot;-1&quot;&gt;ArcGIS Desktop&lt;/h3&gt;
&lt;p&gt;Lo más destacable desde la 9.3.1 es quizá el nuevo formato de mapa &#39;Map Service Definition&#39; (MSD). Parece que viene a sustituir paulatinamente al MXD y se usa en un nuevo motor interno de dibujado que produce mapas bastante más rápidos, tanto en escritorio como en servidor. Otra novedad es la &#39;Map Service Publishing&#39;: una barra de herramientas que parece útil para asesorar (warnings, infos...) sobre cómo hacer documentos de mapa más rápidos de cara a publicación.&lt;/p&gt;
&lt;p&gt;Por otro lado, de la 9.4 se vieron algunos detalles como la integración en Arcmap de una ventana con funciones de ArcCatalog (algo bastante práctico, la verdad), un modo más rápido de edición de features mediante &#39;plantillas&#39; y un nuevo tipo de capa llamado &#39;Query Layer&#39; que permite pintar directamente el resultado de consultas complejas de SQL contra bases de datos estándar (SQL Server y Oracle). Algunos detalles más sobre sus novedades en &lt;a href=&quot;http://www.esri.com/software/arcgis/whats-new/whats-coming.html&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Novedades de desktop 9.4&lt;/a&gt; y &lt;a href=&quot;http://www.unmundoparatodos.com/2009/08/16/arcgis94%5C_novedades/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;http://www.unmundoparatodos.com/2009/08/16/arcgis94&#92;_novedades/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;arc-gis-explorer-900&quot; tabindex=&quot;-1&quot;&gt;ArcGIS Explorer (900)&lt;/h3&gt;
&lt;p&gt;La nueva versión del visor gratuito trae bastantes mejoras estéticas (p.ej. un toolbar tipo &#39;ribbon&#39; como el de ms-office) y alguna funcional (lee .csv, tiene vista 2D/3D, enlaces a fotos/videos, generación de &#39;presentaciones&#39;)...&lt;/p&gt;
&lt;h3 id=&quot;arc-gis-server&quot; tabindex=&quot;-1&quot;&gt;ArcGIS Server&lt;/h3&gt;
&lt;p&gt;Hay una mejora muy importante de rendimiento en los servicios de mapa en la 9.3.1. y avances en el sistema de caché previstos para la v.9.4 (se pueden mezclar formatos de imagen en una misma cache, cuesta mucho menos moverla por la red...). Se avanza igualmente en los APIs para entornos RIA: Flex y Silverlight.&lt;/p&gt;
&lt;p&gt;Mis impresiones personales:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a) El desktop está &#39;tocando techo&#39; en cuanto a funcionalidad. Las mejoras que aparecen se centran en &lt;strong&gt;usabilidad&lt;/strong&gt; (lo cual no me parece mal, la verdad, porque le hace bastante falta) y en ser una buena herramienta para el administrador de ArcGIS Server.&lt;/li&gt;
&lt;li&gt;b) Hay una inversión importante de esri en el &lt;strong&gt;ArcGIS Explorer&lt;/strong&gt;, enfocándolo como un Google Earth, para usuarios no expertos en GIS. Si bien hasta ahora apenas lo he usado, creo que puede ser una herramienta útil para un uso esporádico o para distribuir a ciertos usuarios como visor.&lt;/li&gt;
&lt;li&gt;c) El interés fundamental del desarrollo en esri está en &lt;strong&gt;ArcGIS Server&lt;/strong&gt; y el producto está madurando. El objetivo actual es ganar rendimiento en los servicios de mapa, e ir trasladando funcionalidades del desktop al web. Creo que el próximo objetivo estará en los geoprocesos: primero en que se ejecuten más rápido (ahora mismo no son muy rápidos y parece necesario acudir a desarrollo más complejos como las Server Object Extensions -SOE-).&lt;/li&gt;
&lt;li&gt;d) El &lt;strong&gt;API de Javascript&lt;/strong&gt; para server me sigue pareciendo el más interesante y el camino a seguir (especialmente para dejar de lado el &#39;infierno&#39; del adf para .net. Además, por mucho que veo demos en Silverlight y Flex, no veo diseños ni &amp;quot;efectos&amp;quot; que no se puedan sacar con alguna librería js, y al menos se mantiene uno en los estándares web, sin plugins propietarios.&lt;/li&gt;
&lt;li&gt;d) ESRI está estableciendo &lt;strong&gt;alianzas con Microsoft&lt;/strong&gt; en varios frentes (uso de los servicios de Virtual Earth, el software MapIt, integración con SharePoint...) parece que para posicionarse ante fuertes competidores como Google o el creciente ecosistema de Software libre.&lt;/li&gt;
&lt;/ul&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2009-10-03-conferenciaesri2009/conferencia-de-usuarios-esri-espana-2009/"/>
  </entry><entry>
    <title>Cómo convertir un shapefile a geojson</title>
    <updated>2009-02-02T00:00:00Z</updated>
    <id>https://www.victorvelarde.com/articulos/2009-02-02-shp2geojson/como-convertir-un-shapefile-a-geojson/</id>
    <content type="html">&lt;p&gt;Recientemente necesitábamos publicar una capa geográfica dentro de una web con un sencillo visor de mapas hecho con &lt;a href=&quot;http://openlayers.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Openlayers&lt;/a&gt;. La capa estaba en el formato omnipresente ESRI Shapefile.&lt;/p&gt;
&lt;p&gt;Openlayers acepta un elevado número de orígenes de datos (WMS, WFS, KML, GeoJSON...) pero no directamente ficheros .shp. Una opción hubiera sido utilizar &lt;a href=&quot;http://geoserver.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;Geoserver&lt;/a&gt; como servidor de los datos, pero buscábamos algo más rápido, para una capa estática, y no queríamos incrementar la infraestructura en el despliegue.&lt;/p&gt;
&lt;p&gt;Solución rápida:  generar un fichero con el contenido del .shp en formato geojson y luego alojarlo directamente en el sitio web.  ¿Cómo convertirlo? Utilizando la herramienta gratuita y abierta para conversión entre formatos llamada &lt;a href=&quot;http://www.gdal.org/ogr2ogr.html&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;ogr2ogr&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Descargamos e instalamos &lt;a href=&quot;http://fwtools.maptools.org/&quot; target=&quot;_blank&quot; class=&quot;external-link&quot;&gt;FWTools&lt;/a&gt; que incluye la citada ogr2ogr y otras utilidades.&lt;/li&gt;
&lt;li&gt;Desde línea de comandos, en la ruta donde hayamos instalado el software (o la incluimos en el PATH y tecleamos desde cualquier directorio): &lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;ogr2ogr c:&#92;&#92;ficheroSalida.geojson -f “GeoJSON” C:&#92;&#92;ficheroOriginal.shp
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Y con esto ya tenemos un fichero geojson apto para consumir desde OpenLayers.&lt;/li&gt;
&lt;/ol&gt;
</content>
    <link href="https://www.victorvelarde.com/articulos/2009-02-02-shp2geojson/como-convertir-un-shapefile-a-geojson/"/>
  </entry></feed>

