<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>/var/log/jynus &#187; jynus</title>
	<atom:link href="http://jynus.com/blog/author/admin/feed/" rel="self" type="application/rss+xml" />
	<link>http://jynus.com/blog</link>
	<description>Blog personal de Jynus</description>
	<lastBuildDate>Sun, 30 Aug 2009 10:00:15 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Gráficas circulares SQL</title>
		<link>http://jynus.com/blog/2009/08/graficas-circulares-sql/</link>
		<comments>http://jynus.com/blog/2009/08/graficas-circulares-sql/#comments</comments>
		<pubDate>Sat, 15 Aug 2009 11:50:19 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[ascii]]></category>
		<category><![CDATA[circulares]]></category>
		<category><![CDATA[graficas]]></category>
		<category><![CDATA[shlomi]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[traduccion]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=110</guid>
		<description><![CDATA[Este es el segundo post de creación de gráficos con SQL de Shlomi Noach. Puedes leer también el original en inglés en su blog y mi post anterior. Abajo se muestra una gráfica circular generada mediante (una única consulta) SQL. Recorreré los pasos para producirla, y concluiré con, espero que estén de acuerdo conmigo, algunos [...]]]></description>
			<content:encoded><![CDATA[<p><em>Este es el segundo post de creación de gráficos con SQL de </em><em><a title="Shlomi Noach" href="http://code.openark.org/blog/">Shlomi Noach</a>. Puedes leer también el <a href="http://code.openark.org/blog/mysql/sql-pie-chart">original en inglés en su blog</a> y <a href="http://jynus.com/blog/2009/08/dibujos-sql/">mi post anterior</a>.</em></p>
<p>Abajo se muestra una gráfica circular generada mediante (una única consulta) SQL. Recorreré los pasos para producirla, y concluiré con, espero que estén de acuerdo conmigo, algunos ejemplos útiles para el mundo real.</p>
<pre>+----------------------------------------------------------------------+
| pie_chart                                                            |
+----------------------------------------------------------------------+
|                                                                      |
|                         ;;;;;;;;;;;;;;;;;;;;;                        |
|                  oooooooo;;;;;;;;;;;;;;;;;;;;;;;;;;;                 |
|             oooooooooooooo;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;            |
|          ooooooooooooooooo                 ;;;;;;;;;;;;#####         |
|        oooooooooooooo                           ;#############       |
|       oooooooooooo                                 ############      |
|      oooooooooooo                                   ############     |
|      ooooooooooo                                     ###########     |
|      oooooooooooo                                   ::::::::::::     |
|       oooooooooooo                                 ::::::::::::      |
|        ooooooooo:::::                           ::::::::::::::       |
|          o::::::::::::::::                 :::::::::::::::::         |
|             :::::::::::::::::::::::::::::::::::::::::::::            |
|                  :::::::::::::::::::::::::::::::::::                 |
|                         :::::::::::::::::::::                        |
|                                                                      |
| ##  red: 1 (10%)                                                     |
| ;;  blue: 2 (20%)                                                    |
| oo  orange: 3 (30%)                                                  |
| ::  white: 4 (40%)                                                   |
+----------------------------------------------------------------------+</pre>
<h4><span id="more-110"></span>Requisitos</h4>
<p>Necesitamos generar una consulta genérica que devuelva al menos dos columnas: <strong>name_column</strong> (un nombre) y <strong>value_column</strong> (un valor). Por ejemplo, la siguiente consulta servirá:</p>
<pre>SELECT name AS name_column, value AS value_column FROM sample_values2;
+-------------+--------------+
| name_column | value_column |
+-------------+--------------+
| red         |            1 |
| blue        |            2 |
| orange      |            3 |
| white       |            4 |
+-------------+--------------+</pre>
<p>Encontrará los datos de ejemplo en <a href="http://code.openark.org/blog/wp-content/uploads/2009/08/pie_data.sql">pie_data.sql</a>.</p>
<h4>Parte 1: expandir la consulta original</h4>
<p>Tomaremos los resultados de la consulta anterior y los expandimos: ¿cuál es el ratio respecto al total, por cada valor? Como primer paso, los valores acumulados:</p>
<pre>SELECT
  name_column,
  value_column,
  @accumulating_value := @accumulating_value+value_column AS accumulating_value
FROM (
  SELECT name AS name_column, value AS value_column FROM sample_values2
  ) select_values,
  (SELECT @accumulating_value := 0) select_accumulating_value
;
+-------------+--------------+--------------------+
| name_column | value_column | accumulating_value |
+-------------+--------------+--------------------+
| red         |            1 |                  1 |
| blue        |            2 |                  3 |
| orange      |            3 |                  6 |
| white       |            4 |                 10 |
+-------------+--------------+--------------------+</pre>
<p>A continuación, calculamos el ratio del valor acumulado, y lo presentamos tanto en el rango [0..1], como en el [0..2*PI] (radianes):</p>
<pre>SELECT
  name_order,
  name_column,
  value_column,
  accumulating_value,
  accumulating_value/@accumulating_value AS accumulating_value_ratio,
  2*PI()*accumulating_value/@accumulating_value AS accumulating_value_radians
FROM (
  SELECT
    name_column,
    value_column,
    @name_order := @name_order+1 AS name_order,
    @accumulating_value := @accumulating_value+value_column AS accumulating_value,
    @aggregated_name_column := CONCAT(@aggregated_name_column, name_column, ',') AS aggregated_name_column
  FROM (
    SELECT name AS name_column, value AS value_column FROM sample_values2
    ) select_values,
    (SELECT @name_order := 0) select_name_order,
    (SELECT @accumulating_value := 0) select_accumulating_value,
    (SELECT @aggregated_name_column := '') select_aggregated_name_column
  ) select_accumulating_values
;
+------------+-------------+--------------+--------------------+--------------------------+----------------------------+
| name_order | name_column | value_column | accumulating_value | accumulating_value_ratio | accumulating_value_radians |
+------------+-------------+--------------+--------------------+--------------------------+----------------------------+
|          1 | red         |            1 |                  1 |                      0.1 |           0.62831853071796 |
|          2 | blue        |            2 |                  3 |                      0.3 |            1.8849555921539 |
|          3 | orange      |            3 |                  6 |                      0.6 |            3.7699111843078 |
|          4 | white       |            4 |                 10 |                        1 |            6.2831853071796 |
+------------+-------------+--------------+--------------------+--------------------------+----------------------------+</pre>
<p>El valor de los radianes nos ayudará a decidir dónde estará cada valor en la gráfica circular.</p>
<h4>Parte 2: Entre bastidores de la gráfica circular</h4>
<p>Ahora explicaremos cómo funciona la gráfica circular. Más tarde, lo combinaremos con la <strong>Parte 1</strong><strong></strong>, para producir la gráfica final.</p>
<p>Primero, generamos el sistema de coordenadas (ver <a href="http://jynus.com/blog/2009/08/dibujos-sql/">Dibujos SQL</a>):</p>
<pre>SELECT
  GROUP_CONCAT(CONCAT(t2.value,'.',t1.value) order by t1.value separator ' ') as circle
FROM
  tinyint_asc t1,
  tinyint_asc t2,
  (select @size := 10) sel_size,
  (select @radius := (@size/2 - 1)) sel_radius
WHERE
  t1.value &lt; @size
  AND t2.value &lt; @size
GROUP BY t2.value
;
+-----------------------------------------+
| circle                                  |
+-----------------------------------------+
| 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 |
| 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 |
| 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 |
| 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 |
| 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 |
| 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 |
| 6.0 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 |
| 7.0 7.1 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 |
| 8.0 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9 |
| 9.0 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9 |
+-----------------------------------------+</pre>
<p>En lo que es un gran paso adelante, calculamos el ángulo por coordenada, en relación con el centro, del sistema de coordenadas. El cálculo se hace en radianes, pero se muestra en grados para mejor legibilidad. También anotamos en qué cuadrante de la gráfica está cada punto.</p>
<pre>SELECT
  group_concat(
    round(radians*180/PI())
    order by col_number separator ' ') as circle
FROM (
  SELECT
    t1.value AS col_number,
    t2.value AS row_number,
    @dx := (t1.value - (@size-1)/2) AS dx,
    @dy := ((@size-1)/2 - t2.value) AS dy,
    @abs_radians := IF(@dx = 0, PI()/2, (atan(abs(@dy/@dx)))) AS abs_radians,
    CASE
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &gt;= 0 THEN @abs_radians
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()-@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()+@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &gt;= 0 THEN 2*PI()-@abs_radians
    END AS radians
  FROM
    tinyint_asc t1,
    tinyint_asc t2,
    (select @size := 15) sel_size,
    (select @radius := (@size/2 - 1)) sel_radius
  WHERE
    t1.value &lt; @size
    AND t2.value &lt; @size) select_combinations
  GROUP BY row_number
;
+-------------------------------------------------------------+
| circle                                                      |
+-------------------------------------------------------------+
| 135 131 126 120 113 106 98 90 82 74 67 60 54 49 45          |
| 139 135 130 124 117 108 99 90 81 72 63 56 50 45 41          |
| 144 140 135 129 121 112 101 90 79 68 59 51 45 40 36         |
| 150 146 141 135 127 117 104 90 76 63 53 45 39 34 30         |
| 157 153 149 143 135 124 108 90 72 56 45 37 31 27 23         |
| 164 162 158 153 146 135 117 90 63 45 34 27 22 18 16         |
| 172 171 169 166 162 153 135 90 45 27 18 14 11 9 8           |
| 180 180 180 180 180 180 180 90 0 0 0 0 0 0 0                |
| 188 189 191 194 198 207 225 270 315 333 342 346 349 351 352 |
| 196 198 202 207 214 225 243 270 297 315 326 333 338 342 344 |
| 203 207 211 217 225 236 252 270 288 304 315 323 329 333 337 |
| 210 214 219 225 233 243 256 270 284 297 307 315 321 326 330 |
| 216 220 225 231 239 248 259 270 281 292 301 309 315 320 324 |
| 221 225 230 236 243 252 261 270 279 288 297 304 310 315 319 |
| 225 229 234 240 247 254 262 270 278 286 293 300 306 311 315 |
+-------------------------------------------------------------+</pre>
<p>A esto se le necesita dar formato para una mejor presentación, pero ahora no es el objetivo; sólo lo estoy enseñando para explicar los pasos tomados.</p>
<h4>Parte 3: combinando los dos</h4>
<p>El siguiente paso es probablemente el más importante, vamos a presentar una tosca gráfica circular <em>cuadrada</em> usando los valores originales:</p>
<pre>SELECT
  group_concat(
    (SELECT name_order FROM
      (
      SELECT
        name_order,
        name_column,
        value_column,
        accumulating_value,
        accumulating_value/@accumulating_value AS accumulating_value_ratio,
        2*PI()*accumulating_value/@accumulating_value AS accumulating_value_radians
      FROM (
        SELECT
          name_column,
          value_column,
          @name_order := @name_order+1 AS name_order,
          @accumulating_value := @accumulating_value+value_column AS accumulating_value,
          @aggregated_name_column := CONCAT(@aggregated_name_column, name_column, ',') AS aggregated_name_column
        FROM (
          SELECT name AS name_column, value AS value_column FROM sample_values2
          ) select_values,
          (SELECT @name_order := 0) select_name_order,
          (SELECT @accumulating_value := 0) select_accumulating_value,
          (SELECT @aggregated_name_column := '') select_aggregated_name_column
        ) select_accumulating_values
      ) select_for_radians
    WHERE accumulating_value_radians &gt;= radians LIMIT 1
    )
    order by col_number separator ' ') as circle
FROM (
  SELECT
    t1.value AS col_number,
    t2.value AS row_number,
    @dx := (t1.value - (@size-1)/2) AS dx,
    @dy := ((@size-1)/2 - t2.value) AS dy,
    @abs_radians := IF(@dx = 0, PI()/2, (atan(abs(@dy/@dx)))) AS abs_radians,
    CASE
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &gt;= 0 THEN @abs_radians
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()-@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()+@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &gt;= 0 THEN 2*PI()-@abs_radians
    END AS radians
  FROM
    tinyint_asc t1,
    tinyint_asc t2,
    (select @size := 21) sel_size,
    (select @radius := (@size/2 - 1)) sel_radius
  WHERE
    t1.value &lt; @size
    AND t2.value &lt; @size) select_combinations
  GROUP BY row_number
;
+-------------------------------------------+
| circle                                    |
+-------------------------------------------+
| 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 |
| 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 |
| 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 |
| 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 1 |
| 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 |
| 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 |
| 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 1 1 1 1 1 |
| 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 |
| 3 3 3 3 3 3 3 3 3 3 2 2 2 1 1 1 1 1 1 1 1 |
| 3 3 3 3 3 3 3 3 3 3 2 2 1 1 1 1 1 1 1 1 1 |
| 3 3 3 3 3 3 3 3 3 3 2 1 1 1 1 1 1 1 1 1 1 |
| 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 |
| 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
| 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 |
+-------------------------------------------+</pre>
<p>El truco SQL es que <em>todo va dentro de la expresión GROUP_CONCAT</em>. En vez de presentar la coordenda, miramos en la tabla de valores modificada, buscando la primera fila que tiene un ángulo mayor (en radianes) que el pixel actual. Entonces mostramos <strong>1</strong>, <strong>2</strong>, etc. para denotar el valor.</p>
<p>El siguiente paso es realmente muy simple: ¡en vez de dibujar el cuadrado completo, lo limitamos a un círculo!</p>
<pre>SELECT
  group_concat(
    IF(round(sqrt(pow(col_number-(@size-1)/2, 2) + pow(row_number-(@size-1)/2, 2))) BETWEEN @radius/2 AND @radius,
    (SELECT name_order FROM
      (
      SELECT
        name_order,
        name_column,
        value_column,
        accumulating_value,
        accumulating_value/@accumulating_value AS accumulating_value_ratio,
        2*PI()*accumulating_value/@accumulating_value AS accumulating_value_radians
      FROM (
        SELECT
          name_column,
          value_column,
          @name_order := @name_order+1 AS name_order,
          @accumulating_value := @accumulating_value+value_column AS accumulating_value,
          @aggregated_name_column := CONCAT(@aggregated_name_column, name_column, ',') AS aggregated_name_column
        FROM (
          SELECT name AS name_column, value AS value_column FROM sample_values2
          ) select_values,
          (SELECT @name_order := 0) select_name_order,
          (SELECT @accumulating_value := 0) select_accumulating_value,
          (SELECT @aggregated_name_column := '') select_aggregated_name_column
        ) select_accumulating_values
      ) select_for_radians
    WHERE accumulating_value_radians &gt;= radians LIMIT 1
    ), '-')
    order by col_number separator ' ') as circle
FROM (
  SELECT
    t1.value AS col_number,
    t2.value AS row_number,
    @dx := (t1.value - (@size-1)/2) AS dx,
    @dy := ((@size-1)/2 - t2.value) AS dy,
    @abs_radians := IF(@dx = 0, PI()/2, (atan(abs(@dy/@dx)))) AS abs_radians,
    CASE
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &gt;= 0 THEN @abs_radians
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()-@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()+@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &gt;= 0 THEN 2*PI()-@abs_radians
    END AS radians
  FROM
    tinyint_asc t1,
    tinyint_asc t2,
    (select @size := 21) sel_size,
    (select @radius := 7) sel_radius
  WHERE
    t1.value &lt; @size
    AND t2.value &lt; @size) select_combinations
  GROUP BY row_number
;
+-------------------------------------------+
| circle                                    |
+-------------------------------------------+
| - - - - - - - - - - - - - - - - - - - - - |
| - - - - - - - - - - - - - - - - - - - - - |
| - - - - - - - - - - - - - - - - - - - - - |
| - - - - - - - - 2 2 2 2 2 - - - - - - - - |
| - - - - - - 3 3 3 2 2 2 2 2 2 - - - - - - |
| - - - - - 3 3 3 3 2 2 2 2 2 2 2 - - - - - |
| - - - - 3 3 3 3 3 2 2 2 2 2 2 2 1 - - - - |
| - - - - 3 3 3 3 3 - - - 2 2 2 1 1 - - - - |
| - - - 3 3 3 3 3 - - - - - 1 1 1 1 1 - - - |
| - - - 3 3 3 3 - - - - - - - 1 1 1 1 - - - |
| - - - 3 3 3 3 - - - - - - - 1 1 1 1 - - - |
| - - - 3 3 3 3 - - - - - - - 4 4 4 4 - - - |
| - - - 3 3 3 3 3 - - - - - 4 4 4 4 4 - - - |
| - - - - 3 3 4 4 4 - - - 4 4 4 4 4 - - - - |
| - - - - 3 4 4 4 4 4 4 4 4 4 4 4 4 - - - - |
| - - - - - 4 4 4 4 4 4 4 4 4 4 4 - - - - - |
| - - - - - - 4 4 4 4 4 4 4 4 4 - - - - - - |
| - - - - - - - - 4 4 4 4 4 - - - - - - - - |
| - - - - - - - - - - - - - - - - - - - - - |
| - - - - - - - - - - - - - - - - - - - - - |
| - - - - - - - - - - - - - - - - - - - - - |
+-------------------------------------------+</pre>
<p>Esto ya se parece más a una gráfica circular.</p>
<h4>Part 4: añadiendo los detalles</h4>
<p>Ahora (en un sólo paso) añadiremos:</p>
<ul>
<li>Redimensionado en el eje X.</li>
<li>Eliminación de los espacios.</li>
<li>Coloreado de las secciones de la gráfica.</li>
<li>Una leyenda</li>
</ul>
<p>El  texto en <strong>negrita</strong> es la consulta original, y es lo único que tendrías que cambiar para crear tus propias gráficas circulares.</p>
<pre>SELECT
  group_concat(
    IF(round(sqrt(pow(col_number/@stretch-0.5-(@size-1)/2, 2) + pow(row_number-(@size-1)/2, 2))) BETWEEN @radius*2/3 AND @radius,
    (SELECT SUBSTRING(@colors, name_order, 1) FROM
      (
      SELECT
        name_order,
        name_column,
        value_column,
        accumulating_value,
        accumulating_value/@accumulating_value AS accumulating_value_ratio,
        @aggregated_data := CONCAT(@aggregated_data, name_column, ': ', value_column, ' (', ROUND(100*value_column/@accumulating_value), '%)', '|') AS aggregated_name_column,
        2*PI()*accumulating_value/@accumulating_value AS accumulating_value_radians
      FROM (
        SELECT
          name_column,
          value_column,
          @name_order := @name_order+1 AS name_order,
          @accumulating_value := @accumulating_value+value_column AS accumulating_value
        FROM (
          <strong>SELECT name AS name_column, value AS value_column FROM sample_values2 LIMIT 4</strong>
          ) select_values,
          (SELECT @name_order := 0) select_name_order,
          (SELECT @accumulating_value := 0) select_accumulating_value,
          (SELECT @aggregated_data := '') select_aggregated_name_column
        ) select_accumulating_values
      ) select_for_radians
    WHERE accumulating_value_radians &gt;= radians LIMIT 1
    ), ' ')
    order by col_number separator '') as pie
FROM (
  SELECT
    t1.value AS col_number,
    t2.value AS row_number,
    @dx := (t1.value/@stretch - (@size-1)/2) AS dx,
    @dy := ((@size-1)/2 - t2.value) AS dy,
    @abs_radians := IF(@dx = 0, PI()/2, (atan(abs(@dy/@dx)))) AS abs_radians,
    CASE
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &gt;= 0 THEN @abs_radians
      WHEN SIGN(@dy) &gt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()-@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &lt;= 0 THEN PI()+@abs_radians
      WHEN SIGN(@dy) &lt;= 0 AND SIGN(@dx) &gt;= 0 THEN 2*PI()-@abs_radians
    END AS radians
  FROM
    tinyint_asc t1,
    tinyint_asc t2,
    (select @size := 23) sel_size,
    (select @radius := (@size/2 - 1)) sel_radius,
    (select @stretch := 4) sel_stretch,
    (select @colors := '#;o:X"@+-=123456789abcdef') sel_colors
  WHERE
    t1.value &lt; @size*@stretch
    AND t2.value &lt; @size) select_combinations
  GROUP BY row_number
UNION ALL
  SELECT
    CONCAT(
      REPEAT(SUBSTRING(@colors, value, 1), 2),
      '  ',
      SUBSTRING_INDEX(SUBSTRING_INDEX(@aggregated_data, '|', value), '|', -1)
    )
  FROM
    tinyint_asc
  WHERE
    value BETWEEN 1 AND @name_order
;
+----------------------------------------------------------------------------------------------+
| pie                                                                                          |
+----------------------------------------------------------------------------------------------+
|                                                                                              |
|                                   ;;;;;;;;;;;;;;;;;;;;;;;;;                                  |
|                          oooooooo;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                         |
|                    ooooooooooooooo;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                   |
|                oooooooooooooooooooo;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;               |
|             oooooooooooooooooooooooo                     ;;;;;;;;;;;;;;;;;;;;;###            |
|           oooooooooooooooooooo                                 ;;;;;;;;;###########          |
|         oooooooooooooooooo                                         ##################        |
|       ooooooooooooooooo                                               #################      |
|      ooooooooooooooooo                                                 #################     |
|      oooooooooooooooo                                                   ################     |
|     oooooooooooooooo                                                     ################    |
|      oooooooooooooooo                                                   ::::::::::::::::     |
|      ooooooooooooooooo                                                 :::::::::::::::::     |
|       ooooooooooooooooo                                               :::::::::::::::::      |
|         oooooooooooooo::::                                         ::::::::::::::::::        |
|           ooooooo:::::::::::::                                 ::::::::::::::::::::          |
|             ::::::::::::::::::::::::                     ::::::::::::::::::::::::            |
|                :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::               |
|                    :::::::::::::::::::::::::::::::::::::::::::::::::::::::                   |
|                          :::::::::::::::::::::::::::::::::::::::::::                         |
|                                   :::::::::::::::::::::::::                                  |
|                                                                                              |
| ##  red: 1 (10%)                                                                             |
| ;;  blue: 2 (20%)                                                                            |
| oo  orange: 3 (30%)                                                                          |
| ::  white: 4 (40%)                                                                           |
+----------------------------------------------------------------------------------------------+</pre>
<p>Realizar la leyenda es por si mismo un truco interesante: agregar un número desconocido de filas usando una variable de sesión, para luego volverlas a separar. Por supuesto, esto sólo funciona bien si tenemos un número pequeño de filas (valores), tal y como esperábamos en nuestro caso.</p>
<h4>Ejemplos</h4>
<p>De acuerdo. ¿Por qué? <em>¡Por qué?</em></p>
<p>Aquí hay tres gráficas que espero que convenzan al lector escéptico:</p>
<p><em>Dada la base de datos <strong>sakila</strong></em><em>, lista las 8 tablas de mayor tamaño (en KB):<br />
</em>[NdT: la base de datos sakila se trata de una <a href="http://dev.mysql.com/doc/sakila/en/sakila.html">base de datos de ejemplo de MySQL</a>]</p>
<pre>+------------------------------------------------------------------------------+
| pie_chart                                                                    |
+------------------------------------------------------------------------------+
|                                                                              |
|                            #######################                           |
|                    #######################################                   |
|               #################################################              |
|            ##################                   ##################           |
|         ###############                               ###############        |
|        ;;###########                                     #############       |
|      ;;;;;;;;;;;;;                                         #############     |
|      ;;;;;;;;;;;;                                           ############     |
|     ;;;;;;;;;;;;;                                           #############    |
|      ;;;;;;;;;;;;                                           @@@@@@@@@@@@     |
|      ;;;;;;;;;;;;;                                         """""""""""""     |
|        ;;;;;;;;;;;;;                                     XX"""""""""""       |
|         ;;;;;;;;;;;;;;;                               XXXXXXXXXXXXX""        |
|            ;;;;;;;;;;;;;;;;;;                   ::::::::::XXXXXXXX           |
|               ;;;;;;;;;;;;;;;;;;;;;;;;;;oooooooooo::::::::::::X              |
|                    ;;;;;;;;;;;;;;;;;;;;;;ooooooooooo::::::                   |
|                            ;;;;;;;;;;;;;;ooooooooo                           |
|                                                                              |
| ##  rental: 2850816 (43%)                                                    |
| ;;  payment: 2228224 (34%)                                                   |
| oo  inventory: 376832 (6%)                                                   |
| ::  film_text: 325440 (5%)                                                   |
| XX  film: 278528 (4%)                                                        |
| ""  film_actor: 278528 (4%)                                                  |
| @@  customer: 131072 (2%)                                                    |
| ++  staff: 98304 (1%)                                                        |
+------------------------------------------------------------------------------+</pre>
<p><em>¿Cuánto espacio en disco consume cada motor de base de datos (suma de los tamaños de tabla por motor)?</em></p>
<pre>+------------------------------------------------------------------------------+
| pie_chart                                                                    |
+------------------------------------------------------------------------------+
|                                                                              |
|                            #######################                           |
|                    #######################################                   |
|               #################################################              |
|            ##################                   ##################           |
|         ###############                               ###############        |
|        #############                                     #############       |
|      #############                                         #############     |
|      ############                                           ############     |
|     #############                                           #############    |
|      ############                                           oooooooooooo     |
|      #############                                         ;;;;;;;;;;;oo     |
|        #############                                     ;;;;;;;;;;;;;       |
|         ###############                               ;;;;;;;;;;;;;;;        |
|            ##################                   #;;;;;;;;;;;;;;;;;           |
|               #####################################;;;;;;;;;;;;              |
|                    ###################################;;;;                   |
|                            #######################                           |
|                                                                              |
| ##  InnoDB: 1908732 (84%)                                                    |
| ;;  MyISAM: 284074 (12%)                                                     |
| oo  ARCHIVE: 84276 (4%)                                                      |
+------------------------------------------------------------------------------+</pre>
<p><em>¿Cuáles han sido los tipos de sentencias DML más populares de los últimos 10 segundos?</em></p>
<pre>+------------------------------------------------------------------------------+
| pie_chart                                                                    |
+------------------------------------------------------------------------------+
|                                                                              |
|                            #######################                           |
|                    #######################################                   |
|               #################################################              |
|            ##################                   ##################           |
|         ###############                               ###############        |
|        #############                                     #############       |
|      #############                                         #############     |
|      ############                                           ############     |
|     #############                                           #############    |
|      ############                                           oooo::::::::     |
|      #############                                         ooooooooooooo     |
|        #############                                     ooooooooooooo       |
|         ###############                               ooooooooooooooo        |
|            #################;                   ;;;;;;;;;ooooooooo           |
|               #############;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oo              |
|                    ######;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                   |
|                            ;;;;;;;;;;;;;;;;;;;;;;;                           |
|                                                                              |
| ##  com_select: 1876 (69%)                                                   |
| ;;  com_insert: 514 (19%)                                                    |
| oo  com_delete: 277 (10%)                                                    |
| ::  com_update: 63 (2%)                                                      |
+------------------------------------------------------------------------------+</pre>
<h4>Conclusión</h4>
<p>Los dibujos en ASCII siempre han parecido bastante raros; algunos dirían lo mismo sobre <em>top</em>, <em>wget</em>,  <em>cal</em>, etc. (¿he mencionado <em>lynx</em>?)</p>
<p>Creo que es posible hacer la mayoría de las gráficas más comunes con SQL: ya he mostrado cómo hacer gráficas horizontales y circulares. Podrían generarse también diagramas de barras. Estos métodos no están pensados como una solución permanente; pero es bueno ser capaz de visualizar algunos valores sin tener que instalar Nagios (junto con <em>Apache</em>, <em>Php</em>, drivers, etc.), o tener que exportar la tabla, copiarla a una máquina de escritorio, cargarla en OpenOffice.org Impress y generar las gráficas.</p>
<p>A veces simplemente necesitas echar un vistazo de manera inmediata. Esto es para lo que encuentro útil generar gráficas con SQL.</p>
<p>Por supuesto, Perl y Python ya tienen soluciones para esto, lo cual se hace de manera bastante fácil. Pero haciéndolo directamente desde el cliente de MySQL proporciona, en mi opinión, un mayor nivel de confianza: siempre podrás generar la gráfica, tengas <em>perl-DBD-MySQL</em> o no, en Linux y en Windows.</p>
<p>Además, ha sido divertido.</p>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/08/graficas-circulares-sql/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Dibujos SQL</title>
		<link>http://jynus.com/blog/2009/08/dibujos-sql/</link>
		<comments>http://jynus.com/blog/2009/08/dibujos-sql/#comments</comments>
		<pubDate>Sat, 15 Aug 2009 10:08:22 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[ascii]]></category>
		<category><![CDATA[graficos]]></category>
		<category><![CDATA[shlomi]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[traduccion]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=104</guid>
		<description><![CDATA[Shlomi Noach es también un Instructor, desarrollador y DBA Certificado de MySQL, que vive en Israel. Le he pedido permiso para traducir al español algunas de sus entradas acerca de maneras creativas de usar SQL que leí a través del Planet MySQL. Éste es el primer post de la serie. Podéis leer la entrada original [...]]]></description>
			<content:encoded><![CDATA[<p><em><a title="Shlomi Noach" href="http://code.openark.org/blog/">Shlomi Noach</a> es también un Instructor, desarrollador y DBA Certificado de MySQL, que vive en Israel. Le he pedido permiso para traducir al español algunas de sus entradas acerca de maneras creativas de usar SQL que leí a través del <a href="http://planet.mysql.com/">Planet MySQL</a>. Éste es el primer post de la serie. Podéis leer <a href="http://code.openark.org/blog/mysql/sql-graphics">la entrada original en inglés</a> en su blog.<br />
</em></p>
<p>SQL no está pensado para generar gráficos, por supuesto, pero veo algunos casos en los que generar salidas no tabulares puede ser útil, tal y como mostraré en futuras entradas.</p>
<p>Me gustaría explicar los fundamentos del funcionamiento de los gráficos SQL: de hecho, es posible hacer todo lo que quieras.  ¿Cómo?<span id="more-104"></span></p>
<h4>Sistema de coordenadas</h4>
<p>Desarrollaremos primero un sistema de coordenadas usando SQL. Generando esto, habré probado mi tesis de que cualquier cosa es posible, y proporcionaré además una prueba de concepto.</p>
<p>Para empezar con la generación de coordenadas, necesitaré una tabla de ayuda: una tabla de números (<a href="http://code.openark.org/blog/wp-content/uploads/2009/08/tinyint_asc.sql">tinyint_asc</a>, por ejemplo, con números entre 0 y 255).</p>
<p>Intentaremos producir una matriz de coordenadas 10×10. Para hacer esto, haremos un <em>join</em> de la popia tabla numérica sobre sí misma (<em>autojoin</em> o <em>self-join</em>)<em> </em>, y utilizaremos una variable para establecer el tamaño de la matriz.</p>
<pre>SELECT
  *
FROM
  tinyint_asc numbers1,
  tinyint_asc numbers2,
  (select @size := 10) sel_size
WHERE
  numbers1.value &lt; @size
  AND numbers2.value &lt; @size
;
+-------+-------+-------------+
| value | value | @size := 10 |
+-------+-------+-------------+
|     0 |     0 |          10 |
|     1 |     0 |          10 |
|     2 |     0 |          10 |
|     3 |     0 |          10 |
|     4 |     0 |          10 |
|     5 |     0 |          10 |
|     6 |     0 |          10 |
|     7 |     0 |          10 |
|     8 |     0 |          10 |
|     9 |     0 |          10 |
|     0 |     1 |          10 |
|     1 |     1 |          10 |
|     2 |     1 |          10 |
|     3 |     1 |          10 |
|     4 |     1 |          10 |
|     5 |     1 |          10 |
|     6 |     1 |          10 |
...
|     7 |     8 |          10 |
|     8 |     8 |          10 |
|     9 |     8 |          10 |
|     0 |     9 |          10 |
|     1 |     9 |          10 |
|     2 |     9 |          10 |
|     3 |     9 |          10 |
|     4 |     9 |          10 |
|     5 |     9 |          10 |
|     6 |     9 |          10 |
|     7 |     9 |          10 |
|     8 |     9 |          10 |
|     9 |     9 |          10 |
+-------+-------+-------------+</pre>
<p>El siguiente paso será presentar las coordenadas de una manera ligeramente distinta (esto es sólo para visualizarlo de manera gráfica):</p>
<pre>SELECT
  CONCAT(numbers2.value, '.', numbers1.value)
FROM
  tinyint_asc numbers1,
  tinyint_asc numbers2,
  (select @size := 10) sel_size
WHERE
  numbers1.value &lt; @size
  AND numbers2.value &lt; @size
;
+---------------------------------------------+
| CONCAT(numbers2.value, '.', numbers1.value) |
+---------------------------------------------+
| 0.0                                         |
| 0.1                                         |
| 0.2                                         |
| 0.3                                         |
| 0.4                                         |
| 0.5                                         |
| 0.6                                         |
| 0.7                                         |
| 0.8                                         |
| 0.9                                         |
| 1.0                                         |
| 1.1                                         |
| 1.2                                         |
| 1.3                                         |
| 1.4                                         |
| 1.5                                         |
| 1.6                                         |
| 1.7                                         |
...</pre>
<p>Ya es hora de convertir esto en un sistema de coordenadas:</p>
<pre>SELECT
  GROUP_CONCAT(
    CONCAT(numbers2.value, '.', numbers1.value)
    ORDER BY numbers1.value
    SEPARATOR ' ') AS coordinates
FROM
  tinyint_asc numbers1,
  tinyint_asc numbers2,
  (select @size := 10) sel_size
WHERE
  numbers1.value &lt; @size
  AND numbers2.value &lt; @size
GROUP BY numbers2.value
;
+-----------------------------------------+
| coordinates                             |
+-----------------------------------------+
| 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 |
| 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 |
| 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 |
| 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 |
| 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 |
| 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 |
| 6.0 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 |
| 7.0 7.1 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 |
| 8.0 8.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 8.9 |
| 9.0 9.1 9.2 9.3 9.4 9.5 9.6 9.7 9.8 9.9 |
+-----------------------------------------+</pre>
<p>Esto es todo lo que necesitamos para generar cualquier gráfico que queramos. Usa tu función matemática favorita, aplica la geometría analítica y haz que suceda. <em>En vez de mostrar las coordenadas, úsalas para decidir lo que debería ser mostrado</em>.</p>
<h4>Prueba de concepto</h4>
<p>Para demostrar que la frase anterior es realmente sencilla, mostraré cómo dibujar un círculo usando las coordendas SQL. La famosa fórmula del círculo es:</p>
<blockquote><p>(x-x0)² + (y-y0)² = rad²</p>
<p>siendo <strong>x0</strong>, <strong>y0</strong> las coordendas del centro del círculo y <strong>rad</strong>, el radio.</p></blockquote>
<p>Para aplicarla:</p>
<pre>SELECT
  GROUP_CONCAT(
    IF(
      ROUND(SQRT(POW(numbers1.value-(@size-1)/2, 2) + POW(numbers2.value-(@size-1)/2, 2))) &lt;= @radius,
      '#', '-')
    ORDER BY numbers1.value
    SEPARATOR ' ') AS circle
FROM
  tinyint_asc numbers1,
  tinyint_asc numbers2,
  (select @size := 15) sel_size,
  (select @radius := 6) sel_radius
WHERE
  numbers1.value &lt; @size
  AND numbers2.value &lt; @size
GROUP BY numbers2.value
;
+-------------------------------+
| circle                        |
+-------------------------------+
| - - - - - - - - - - - - - - - |
| - - - - - # # # # # - - - - - |
| - - - # # # # # # # # # - - - |
| - - # # # # # # # # # # # - - |
| - - # # # # # # # # # # # - - |
| - # # # # # # # # # # # # # - |
| - # # # # # # # # # # # # # - |
| - # # # # # # # # # # # # # - |
| - # # # # # # # # # # # # # - |
| - # # # # # # # # # # # # # - |
| - - # # # # # # # # # # # - - |
| - - # # # # # # # # # # # - - |
| - - - # # # # # # # # # - - - |
| - - - - - # # # # # - - - - - |
| - - - - - - - - - - - - - - - |
+-------------------------------+</pre>
<p>¡Elimina el espacio separador, juega con el ratio, aplica tu propia fórmula y genera dibujos SQL-ascii!</p>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/08/dibujos-sql/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>MySQL y los dichosos &#8220;encodings&#8221;</title>
		<link>http://jynus.com/blog/2009/05/mysql-y-los-dichosos-encodings/</link>
		<comments>http://jynus.com/blog/2009/05/mysql-y-los-dichosos-encodings/#comments</comments>
		<pubDate>Fri, 22 May 2009 12:53:30 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Recetas]]></category>
		<category><![CDATA[acii]]></category>
		<category><![CDATA[caracteres]]></category>
		<category><![CDATA[charset]]></category>
		<category><![CDATA[codificacion]]></category>
		<category><![CDATA[encoding]]></category>
		<category><![CDATA[latin1]]></category>
		<category><![CDATA[ucs]]></category>
		<category><![CDATA[utf8]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=93</guid>
		<description><![CDATA[Cualquier persona que comienza a manejar una base de datos suele encontrarse con problemas a la hora de manejar literales de cadena. Si no se pone el debido cuidado, podemos encontrarnos con errores causados por la utilización o conversión de codificaciones de caracteres. Intentaremos dar una pequeña guía para aclarar cómo manejar los distintos &#8216;encodings&#8217; [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_100" class="wp-caption alignright" style="width: 310px"><a href="http://jynus.com/blog/wp-content/uploads/2009/05/errores_codificacion.png"><img class="size-medium wp-image-100" title="errores_codificacion" src="http://jynus.com/blog/wp-content/uploads/2009/05/errores_codificacion-300x182.png" alt="Si no tenemos cuidado, podemos encontrarnos con una desagradable sorpresa en forma de caracteres extraños." width="300" height="182" /></a><p class="wp-caption-text">Si no tenemos cuidado, podemos encontrarnos con una desagradable sorpresa en forma de caracteres extraños.</p></div>
<p>Cualquier persona que comienza a manejar una base de datos suele encontrarse con problemas a la hora de manejar literales de cadena. Si no se pone el debido cuidado, podemos encontrarnos con errores causados por la utilización o conversión de codificaciones de caracteres. Intentaremos dar una pequeña guía para aclarar cómo manejar los distintos &#8216;encodings&#8217; y &#8216;collations&#8217; en una base de datos MySQL.</p>
<h3><span id="more-93"></span>Conceptos básicos</h3>
<p>Definamos, antes de nada, conceptos:</p>
<h4>Juego de caracteres, character set o &#8220;charset&#8221;</h4>
<p>Se trata de un subconjunto definido de símbolos de texto. El más ambicioso de todos ellos es UCS (Universal Character Set), el cual permite representar más de 1 millón de símbolos de forma no ambigua (asignándoles un nombre y número diferente a cada uno), aunque en la práctica apenas se usan más de 65.000.</p>
<h4>Codificación de caracteres o (character) encoding</h4>
<p>Es el algoritmo o la tabla que identifica cada símbolo del juego de caracteres con un valor binario, es decir, indica cómo se almacena de manera digital. Por ejemplo, UCS-2 y UTF-8 son encodings para el mismo juego de caracteres (UCS) <a href="#nota1">[1]</a>, pero la forma de asignarlos varía en cada uno. Mientras que en UTF-8 la representación del caracter puede ocupar entre 1 y 4 bytes <a href="#nota2">[2]</a>, en UCS-2, el tamaño es fijo, de 2 bytes.<br />
Veamos esto de forma práctica: Creemos una tabla con tres encodings diferentes: UTF-8, UCS-2 y ASCII:</p>
<pre>mysql&gt; CREATE TABLE `encoding` (
    -&gt; `utf8` char(1) character set utf8 default NULL,
    -&gt; `ucs2` char(1) character set ucs2 default NULL,
    -&gt; `ascii` char(1) character set ascii default NULL
    -&gt; );</pre>
<p>Insertamos los caracteres &#8216;a&#8217; y &#8216;ñ&#8217;:</p>
<pre>mysql&gt; INSERT INTO encoding VALUES ('a', 'a', 'a');
Query OK, 1 row affected (0.00 sec)</pre>
<pre>mysql&gt; INSERT INTO encoding VALUES ('ñ', 'ñ', 'ñ');
Query OK, 1 row affected, 1 warning (0.00 sec)</pre>
<p>Y vemos cómo están guardados en disco realmente:</p>
<pre>mysql&gt; SELECT utf8 AS `char`, hex(utf8) AS `utf8`, hex(ucs2) AS `ucs2`, hex(ascii) AS `ascii` FROM encoding;
+------+------+------+-------+
| char | utf8 | ucs2 | ascii |
+------+------+------+-------+
| a    | 61   | 0061 | 61    |
| ñ    | C3B1 | 00F1 | 3F    |
+------+------+------+-------+
2 rows in set (0.00 sec)</pre>
<p>Como se puede ver, UTF-8 tiene la ventaja de que para la mayoría de caracteres usados en inglés coincide con la codificación ASCII. Sin embargo, para el resto de caracteres, requiere dos o más bytes <a href="#nota3">[3]</a>).</p>
<p>Algunos de los <em>encodings</em> más usuales son:</p>
<ul>
<li><strong>ASCII</strong> o <strong>US-ASCII</strong>: encoding histórico de 7 bits (aunque existen los denominados encodings &#8220;ASCII extendidos&#8221; que aumentaban el numero de caracteres a 8 bits)</li>
<li><strong>ISO 8859-1</strong> o <strong>Latin 1</strong>: utilizaba 1 byte y, siendo compatible con ASCII, incluía símbolos adicionales para la mayoría de idiomas occidentales (incluyendo la ñ, los acentos y la &#8216;ç&#8217;).</li>
<li><strong>ISO 8859-15</strong> o <strong>Latin 9</strong>: es una revisión de Latin 1, que añade, entre otros, el símbolo del euro.</li>
<li><strong>windows-1252</strong>: versión ligeramente distinta a Latin 1/9 que utilizaban los sistemas operativos Windows</li>
<li><strong>UTF-8</strong>: encoding de la norma Unicode que, siendo compatible con los caracteres ASCII originales, permite reprentar los caracteres de casi cualquier lengua <a href="#nota4">[4]</a> de manera eficiente (sólo requiere más de 1 byte para caracteres fuera de ASCII)</li>
<li><strong>UTF-16</strong>: también de la norma unicode, sólo que requiere siempre 2 bytes para todos los caracteres UCS básicos. Es lo más eficiente si usamos muchos caracteres CJK (chinos, japoneses o koreanos). Se diferencia de UCS-2 en que éste último no podía representar caracteres más allá del número 65.536.</li>
</ul>
<h4>Collation u ordenación</h4>
<p>Es el algoritmo que indica si dos caracteres son equivalentes, o si uno debería ir antes o después en una ordenación. Por ejemplo, en español, si nosotros tuviéramos la siguiente tabla y valores:</p>
<pre>mysql&gt; CREATE TABLE `collation` (`a` char(20) character set utf8 collate utf8_spanish_ci);
mysql&gt; INSERT INTO `collation` VALUES ('amapola');
mysql&gt; INSERT INTO `collation` VALUES ('Andorra');
mysql&gt; INSERT INTO `collation` VALUES ('Ávila');
mysql&gt; INSERT INTO `collation` VALUES ('ábaco');
mysql&gt; INSERT INTO `collation` VALUES ('burro');</pre>
<p>y ejecutáramos la sentencia que nos permite obtener estos valores de manera ordenada, obtendríamos:</p>
<pre>mysql&gt; SELECT * FROM `collation` ORDER BY a;
+---------+
| a       |
+---------+
| ábaco   |
| amapola |
| Andorra |
| Ávila   |
| burro   |
+---------+
5 rows in set (0.00 sec)</pre>
<p>o si preguntáramos por cuales de las palabras empiezan por a, obtenemos:</p>
<pre>mysql&gt; SELECT * FROM `collation` WHERE a LIKE 'a%';
+---------+
| a       |
+---------+
| amapola |
| Andorra |
| Ávila   |
| ábaco   |
+---------+
4 rows in set (0.02 sec)</pre>
<p>Es decir, asume que a == A == á == Á</p>
<p>Esto es así porque, por defecto, el &#8220;collate&#8221; que se le hemos indicado es utf8_spanish_ci. Podemos forzar que use otro algoritmo de ordenamiento de la siguiente manera:</p>
<pre>mysql&gt; SELECT a FROM `collation`ORDER BY a COLLATE utf8_bin;
+---------+
| a       |
+---------+
| Andorra |
| amapola |
| burro   |
| Ávila   |
| ábaco   |
+---------+
5 rows in set (0.00 sec)

mysql&gt; SELECT * FROM `collation` WHERE a LIKE 'a%' COLLATE utf8_bin;
+---------+
| a       |
+---------+
| amapola |
+---------+
1 row in set (0.00 sec)</pre>
<p>Lo cual le indica que utilice exclusivamente para comparaciones el valor binario de su codificación UTF-8.</p>
<h3>Aplicación</h3>
<p>Una vez que ya conocemos los conceptos básicos alrededor de las codificación, vamos a ver cómo los utilizamos en una base de datos.</p>
<p>Existen tres lugares, a distinto nivel, donde podemos configurar el encoding que estamos utilizando para distintas tareas:</p>
<ul>
<li>Variables de sesión</li>
<li>Variables globales</li>
<li>Schemas</li>
<li>Aplicación final cliente</li>
</ul>
<p>La diferencia entre variables globales y de sesión es que las globales se configuran a nivel de servidor y son las que establecen el valor por defecto de las variables de sesión, las cuales pueden modificarse desde el cliente para cada conexión. Las variables de sesión se configuran por conexión, y sólo son válidas mientras ésta esté activa. Si existen variables de sesión y globales con el mismo nombre, las de sesión tienen precedencia.</p>
<p>También podemos asignar encodings y collations a bases de datos, tablas y columnas de manera independiente.</p>
<p>Por último, y aunque no lo trataremos aquí, debemos asegurarnos de que la aplicación que finalmente muestra los resultados lo hace en la misma codificación que hemos recibido los datos. Por ejemplo, si estamos en una sesión con mysql-cliente, tendremos que cambiar la codificación de salida del terminal de manera adecuada.</p>
<p>Veámos más en profundidad a qué afecta y cómo modificarlas en cada nivel.</p>
<h4>Variables</h4>
<p>Son las siguientes:</p>
<pre>mysql&gt; show session variables like '%character\_set\_%';
+--------------------------+--------+
| Variable_name            | Value  |
+--------------------------+--------+
| character_set_client     | utf8   |
| character_set_connection | utf8   |
| character_set_database   | utf8   |
| character_set_filesystem | binary |
| character_set_results    | utf8   |
| character_set_server     | utf8   |
| character_set_system     | utf8   |
+--------------------------+--------+
7 rows in set (0.00 sec)

mysql&gt; show variables like '%collation\_%';
+----------------------+-----------------+
| Variable_name        | Value           |
+----------------------+-----------------+
| collation_connection | utf8_general_ci |
| collation_database   | utf8_general_ci |
| collation_server     | utf8_general_ci |
+----------------------+-----------------+
3 rows in set (0.00 sec)</pre>
<ul>
<li><strong>character_set_client</strong><br />
Es el encoding en el que el servidor asume que le envías los datos. Por defecto es latin1.</li>
<li><strong>character_set_connection</strong> y <strong>collation_connection</strong><br />
character_set_connection es el encoding al que convierte los comandos enviados por el cliente (desde el encoding definido por character_set_client). Por defecto es latin1. collation_connection es el ordenamiento que asume para los literales enviados por el cliente. Es ignorado cuando se trabaja con columnas de tablas, ya que éstos ya tienen definida un &#8220;collate&#8221; propio, el cual tiene prioridad sobre el de la conexión. Como la collation por defecto de latin1 es latin1_swedish_ci, éste es el valor que toma por defecto.</li>
<li><strong>character_set_results</strong><br />
Es el encoding al que el servidor convierte los resultados y mensajes de error antes de enviarlos al cliente</li>
<li><strong>character_set_server</strong> y <strong>collation_server</strong><br />
Es el encoding y collation que usa el servidor por defecto (si no los cambiamos: latin1/latin1_swedish_ci).</li>
<li><strong>character_set_database</strong> y <strong>collation_database</strong><br />
Es el encoding/collation del schema que está en uso actualmente. El servidor la actualiza cada vez que cambiamos la base de datos actual. Podemos cambiarla, pero eso no cambia el encoding por defecto de la base de datos actual. Sus valores por defecto (no tenemos una base de datos selecionada) son los mismos que character_set_server y collation_server (latin1/latin1_swedish_ci, si no los hemos cambiado).</li>
<li><strong>character_set_system</strong><br />
Es el encoding interno usado para los identificadores. Siempre es utf8 (es de sólo lectura).</li>
<li><strong>character_set_filesystem</strong><br />
Es el encoding al que convierte (desde character_set_client para) los nombres de archivo usados en sentencias SELECT&#8230;INTO OUTFILE o LOAD DATA INFILE. Por defecto es binary, con el que no se hace ninguna conversión.</li>
</ul>
<h4>Alterando las opciones de sesión y globales</h4>
<p>Podemos establecer los siguientes valores para alterar el comportamiento por defecto:</p>
<pre>character_set_client
character_set_connection
character_set_filesystem
character_set_results
character_set_server</pre>
<p>Podemos hacerlo de varias manera:</p>
<ul>
<li>Cambiar la variable de sesión en caliente, desde un cliente mysql</li>
</ul>
<pre>SET LOCAL character_set_client='utf8';
SET @@local.character_set_client='utf8';</pre>
<p>Es la opción adecuada si sólo queremos que tenga efecto para la sesión actual. No se mantiene entre sesiones, ni siquiera para el mismo usuario.</p>
<ul>
<li>Cambiar la variable global en caliente, desde un cliente mysql:</li>
</ul>
<pre>SET GLOBAL character_set_client='utf8';
SET @@global.character_set_client='utf8';</pre>
<p>Todas las nuevas conexiones tendrán ese valor por defecto. Tiene el inconveniente de que al reiniciar el servidor, los cambios se perderán.</p>
<ul>
<li>SET NAMES  / SET CHARACTER SET</li>
</ul>
<p>Una forma alternativa -más corta- de cambiar estas variables es utilizando:</p>
<pre>SET NAMES 'x';</pre>
<p>Lo cual es equivalente a hacer:</p>
<pre>SET character_set_client = 'x';
SET character_set_results = 'x';
SET character_set_connection = 'x';</pre>
<p>O bien, de forma muy parecida:</p>
<pre>SET CHARACTER SET 'x';</pre>
<p>Lo cual es equivalente a hacer:</p>
<pre>SET character_set_client = 'x';
SET character_set_results = 'x';
SET character_set_connection = @@collation_database;</pre>
<ul>
<li>Añadir la opción al ejecutar mysqld/mysqld_safe</li>
</ul>
<pre>mysqld --character_set_client='utf8'</pre>
<p>Es necesario hacerlo manualmente (o con algún script) cada vez que iniciemos el servidor.</p>
<ul>
<li>Recompilar el servidor para que por defecto tome un encoding distinto</li>
</ul>
<p>Es muy engorroso sólo para poner una opción</p>
<ul>
<li>Añadir la opción a my.cf o my.ini:</li>
</ul>
<pre>[mysqld]
character_set_client='utf8'</pre>
<p>Es la opción recomendada si queremos que el cambio sea persistente.</p>
<p>Para ver los valores que pueden tomar estas varaibles disponibles, podemos ejecutar:</p>
<pre>SHOW CHARACTER SET [LIKE 'pattern']</pre>
<h4>Schemas</h4>
<p>Podemos cambiar también cómo se guardan los datos en las tablas de la siguiente manera:</p>
<pre>CREATE DATABASE ... DEFAULT CHARACTER SET 'X' COLLATE 'Y' <a href="#nota5">[5]</a>
ALTER DATABASE ... DEFAULT CHARACTER SET 'X' COLLATE 'Y'</pre>
<p>Establece el encoding y collation por defecto para esa base de datos. El cambiarla no altera automáticamente las tablas y columnas dentro de esa base de datos, sino sólo de las que creemos por defecto (sin explicitarlo) a partir de ahora. Por defecto utiliza character_set_server.</p>
<pre>CREATE TABLE ... DEFAULT CHARACTER SET 'X' COLLATE 'Y'
ALTER TABLE ... DEFAULT CHARACTER SET 'X' COLLATE 'Y'</pre>
<p>Establece el encoding y collation por defecto para esa tabla. El cambiarla no altera automáticamente columnas dentro de esa base de datos, sino sólo de las que creemos por defecto (sin explicitarlo) a partir de ahora. Por defecto utiliza el character set de la base de datos a la que pertenece (no de la base de datos actual).</p>
<pre>ALTER TABLE ADD COLUMN ... DEFAULT CHARACTER SET 'X' COLLATE 'Y'
ALTER TABLE ADD MODIFY ... DEFAULT CHARACTER SET 'X' COLLATE 'Y'
ALTER TABLE ADD CHANGE ... DEFAULT CHARACTER SET 'X' COLLATE 'Y'</pre>
<p>Establece el encoding y collation que tendrá realmente la columna. Por defecto utiliza el character set de la tabla a la que pertenece.</p>
<h3>Más información</h3>
<ul>
<li><a href="http://dev.mysql.com/doc/refman/5.0/en/charset-syntax.html">http://dev.mysql.com/doc/refman/5.0/en/charset-syntax.html</a></li>
<li><a href="http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html">http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html</a></li>
<li><a href="http://dev.mysql.com/doc/refman/5.0/en/charset-connection.html">http://dev.mysql.com/doc/refman/5.0/en/charset-connection.html</a></li>
<li><a href="http://en.wikipedia.org/wiki/Character_encoding">http://en.wikipedia.org/wiki/Character_encoding</a></li>
<li><a href="http://en.wikipedia.org/wiki/Unicode">http://en.wikipedia.org/wiki/Unicode</a></li>
</ul>
<h3>Notas</h3>
<ol>
<li><a name="nota1"></a>UCS-2 no permite, siendo estrictos, representar todos los caracteres UCS usados actualmente, por lo que tiende a reemplazarse por UTF-16</li>
<li><a name="nota2"></a>MySQL sólo permite caracteres multibyte UTF-8 de hasta 3 caracteres, lo cual es suficiente para los caracteres que se usan en la práctica</li>
<li><a name="nota3"></a>Es necesario remarcar que tenemos dos funciones en MySQL para ver el tamaño de una cadena: lenght(), que es el tamaño en bytes y char_lenght(), que es el tamaño en caracteres. Ambos pueden diferir.</li>
<li><a name="nota4"></a>Se propuso la adición de los caracteres Klingon a UCS, pero finalmente fue rechazado</li>
<li><a name="nota5"></a>Una sintaxis equivalente, pero más corta para poner un determinado encoding es:
<pre class="wiki">CREATE DATABASE ... CHARSET 'X'</pre>
</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/05/mysql-y-los-dichosos-encodings/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Geolocalizando WIFIs</title>
		<link>http://jynus.com/blog/2009/05/geolocalizando-wifis/</link>
		<comments>http://jynus.com/blog/2009/05/geolocalizando-wifis/#comments</comments>
		<pubDate>Sun, 17 May 2009 20:27:06 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[Actualidad]]></category>
		<category><![CDATA[OSM]]></category>
		<category><![CDATA[Tecnología]]></category>
		<category><![CDATA[geolocalizacion]]></category>
		<category><![CDATA[gps]]></category>
		<category><![CDATA[internet]]></category>
		<category><![CDATA[kismet]]></category>
		<category><![CDATA[wifi]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=74</guid>
		<description><![CDATA[Hoy es, según la ONU, el &#8220;Día Mundial de las Telecomunicaciones y de la Sociedad de la Información&#8221;, aka &#8220;El día de Internet&#8221; aunque, por las noticias que aparecen últimamente en los medios, dudo que haya algo que celebrar. Pero, ¿cuál es la penetración real de Internet en tu barrio? A partir de un comentario [...]]]></description>
			<content:encoded><![CDATA[<p>Hoy es, según la ONU, el <a href="http://identi.ca/notice/4317230">&#8220;Día Mundial de las Telecomunicaciones y de la Sociedad de la Información&#8221;</a>, <span title="Also Known As (también conocido como)">aka</span> &#8220;El día de Internet&#8221; aunque, por <a href="http://www.internautas.org/html/5570.html">las noticias que aparecen últimamente en los medios</a>, dudo que haya algo que celebrar.</p>
<p>Pero, ¿cuál es la <span title="Sí, el término está elegido a mala leche para subir en PageRank">penetración</span> real de Internet en tu barrio? A partir de un comentario que hizo <a href="http://www.capitangolo.net/Blog/">Golo</a> en la <a href="http://www.flickr.com/photos/koke/sets/72157618335689578/">barbacoa</a> del sábado sobre geolocalización a partir de redes WIFI, se me ha ocurrido probar un programa llamado <a href="http://www.kismetwireless.net/">Kismet</a>. He salido esta tarde a dar un paseo cargado con mi AsusEEE y la antena GPS, ambos encendidos, y he dado unas vueltas por un parque cercano.</p>
<p>El resultado de este pequeño experimento es el siguiente:</p>
<div id="attachment_75" class="wp-caption aligncenter" style="width: 310px"><a href="http://jynus.com/blog/wp-content/uploads/2009/05/mapa_wireless.jpg"><img class="size-medium wp-image-75" title="Mapa de las redes Wireless del Barrio" src="http://jynus.com/blog/wp-content/uploads/2009/05/mapa_wireless-300x263.jpg" alt="Mapa Wireless" width="300" height="263" /></a><p class="wp-caption-text">Mapa Wireless</p></div>
<p>Cada círculo de color corresponde a la posición aproximada de un &#8220;Access Point&#8221; o router wifi, y el tamaño del mismo es su cobertura calculada. Para construirlo, se han registrado varias lecturas de intensidad de las redes (algo completamente legal, ya que se trata de un rango de frecuencias no regulado) y se han renderizado sobre un mapa de OpenStreetMap. En total se han detectado casi 300 redes WIFI (sólo algunas de ellas se han podido representar), y eso que sólo di una vuelta a la manzana. Si a alguien le interesa el proceso, puedo escribir un nuevo post explicando cómo lo he hecho, con más detalle.</p>
<p>Luego la respuesta es que sí, parece que, pese a <a href="http://www.consumer.es/web/es/tecnologia/2009/04/21/184847.php">tener la banda ancha más cara de Europa</a>, ésta ha llegado al hogar de manera considerable.</p>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/05/geolocalizando-wifis/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Navegando con GPS y OSM</title>
		<link>http://jynus.com/blog/2009/03/navegando-con-gps-y-osm/</link>
		<comments>http://jynus.com/blog/2009/03/navegando-con-gps-y-osm/#comments</comments>
		<pubDate>Fri, 06 Mar 2009 13:49:07 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[OSM]]></category>
		<category><![CDATA[Recetas]]></category>
		<category><![CDATA[bluetooth]]></category>
		<category><![CDATA[cloudmade]]></category>
		<category><![CDATA[eeepc]]></category>
		<category><![CDATA[googlemaps]]></category>
		<category><![CDATA[gps]]></category>
		<category><![CDATA[gpsd]]></category>
		<category><![CDATA[navit]]></category>
		<category><![CDATA[openstreetmap]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=57</guid>
		<description><![CDATA[Con ganas de sacarle partido al EeePC que me compré hace unas semanas, y previendo que voy a tener que viajar bastante durante este año, me he dedicado a buscar una manera de tener un navegador GPS gratuito y legal para este cacharro. Google Maps no está mal, pero tiene los siguientes inconvenientes: Sus términos [...]]]></description>
			<content:encoded><![CDATA[<p>Con ganas de sacarle partido al <a href="http://eeepc.asus.com/global/product901.html?n=0">EeePC</a> que me compré hace unas semanas, y <a href="http://jynus.com/blog/2009/02/ya-soy-warper/">previendo que voy a tener que viajar bastante</a> durante este año, me he dedicado a buscar una manera de tener un navegador GPS gratuito y legal para este cacharro.</p>
<p>Google Maps no está mal, pero tiene los siguientes inconvenientes:</p>
<ol>
<li>Sus términos de uso <a href="http://www.google.com/intl/es_es/help/terms_maps.html">prohíben explícitamente estas prácticas</a></li>
<li>Los datos de Teleatlas (usados por el callejero de Google) dan mucha penita a veces (mirad dónde han puesto la <a href="http://maps.google.com/maps?q=soria&amp;oe=utf-8&amp;client=iceweasel-a&amp;ie=UTF8&amp;split=0&amp;ei=ChaxSbnIAYiyjAeyj9nxBQ&amp;ll=41.764398,-2.474467&amp;spn=0.005634,0.014677&amp;t=h&amp;z=16">Alameda Cervantes</a>, en Soria, o la pinta que tiene la <a href="http://maps.google.com/maps?hl=es&amp;ll=41.65596,-0.877898&amp;spn=0.002822,0.007339&amp;z=17">Plaza del Pilar</a> de Zaragoza). Además, se centran mucho en información útil para automóviles y poco o nada en peatones y bicicletas</li>
<li>Sólo ofrecen públicamente imágenes (&#8220;tiles&#8221;) de los mapas, no los datos originales vectoriales. Eso hace imposible el crear rutas sin estar conectado a internet.</li>
</ol>
<p>Así que decidí probar <a href="http://www.navit-project.org/"><strong>Navit</strong></a>, un programa de navegación GPS libre que permite utilizar los datos de <a href="http://www.openstreetmap.org">OpenStreetMap</a> para mostar mapas y calcular rutas. Aunque está todavía en desarrollo y hay cosas que no funcionan muy bien, tiene todo lo que yo quería: mostrar mapas vectoriales, buscar direcciones y calcular rutas.</p>
<p><strong>Primer paso</strong>: descargar e instalar el programa. En Debian es muy fácil, ya que existe un repositorio:</p>
<pre># navit repository
deb http://navit.latouche.info/debian lenny main</pre>
<p>Que simplemente tenmos que añadir a nuestro <code>/etc/apt/sources.list</code>, seguido del clásico:</p>
<pre>$ sudo aptitude update
$ sudo aptitude install navit</pre>
<p><strong>Paso 2</strong>: conectar el GPS Buetooth al EeePC. Es necesario activar el Bluetooth desde la BIOS (todavía no he encontrado la manera de desactivarlo/activarlo desde el sistema operativo). También necesitaremos tener instaladas las utilidades para gestionar el GPS y la conexión inalámbrica, principalmente <code>bluez-utils</code> y <code>gpsd</code>. Después, emparejaremos la antena bluetooth a nuestro dispositivo y finalmente configuraremos el demonio de GPS para que utilice esta señal:</p>
<pre>$ sudo aptitude install gpsd bluez-utils # instalacion de paquetes
$ sudo hcitool dev # permite ver los dispositivos BT
Devices:
        hci0    00:11:22:33:44:55
$ sudo hciconfig hci0 up # levanta el interfaz bluetooth
$ sudo hcitool scan # permite ver los dispositivos al alcance
Scanning ...
        00:99:88:77:66:55       BT GPS # &lt;- este es el GPS</pre>
<p>Editamos el archivo <code>/etc/bluetooth/rfcomm.conf</code> y lo rellenamos con la dirección hardware que hemos obtenido en el paso anterior:</p>
<pre>#
# RFCOMM configuration file.
#

rfcomm0 {
        # Automatically bind the device at startup
        bind no;

        # Bluetooth address of the device
        device 00:99:88:77:66:55;

        # RFCOMM channel for the connection
        channel 1;

        # Description of the connection
        comment "Antena GPS Bluetooth";
}</pre>
<p>Lo vinculamos:</p>
<pre>$ sudo rfcomm bind rfcomm0</pre>
<p>Y ahora lo configuramos para que lo reconozca gpsd, ejecutando:</p>
<pre>$ sudo dpkg-reconfigure gpsd</pre>
<p>Y escribiendo <code>/dev/rfcomm0</code> cuando pregunte por el dispositivo.<br />
Podemos comprobar que funciona así:</p>
<pre>$ telnet localhost 2947
r # escribir r + intro
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,132515.936,V,XXXXX,N,XXXXXX,W,0.00,0.00,060309,,,N*6C
$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32
$GPRMC,132515.936,V,XXXXX,N,XXXXXX,W,0.00,0.00,060309,,,N*6C
$GPGSV,1,1,01,23,,,19*71
[...]</pre>
<p><strong>Paso 3</strong>: descargar los mapas y programa</p>
<p>Por alguna razón, ejecutar navit sin configurar antes GPS y mapas hace que este falle. Los mapas que utiliza navit (.bin) pueden obtenerse a partir de archivos de datos de  OpenStreetMap (.osm) -existe <a href="http://maps.navit-project.org/download/">una utilidad online</a> que lo hace automáticamente- o bien descargarse directamente ya convertidos al formato binario de Navit desde sitios como <a href="http://downloads.cloudmade.com/europe/spain">Cloudmade</a>. Yo he optado por esto último y he bajado el archivo <a href="http://downloads.cloudmade.com/europe/spain/spain.navit.bin.zip">spain.navit.bin.zip</a> (que tiene todos los datos de España), lo he descomprimido y lo he guardado en la memoria flash del EEE.</p>
<p>Por último, he editado el archivo <code>/etc/navit/navit.xml</code> para que lo use:</p>
<pre>&lt;!-- Mapset template for openstreetmaps --&gt;
&lt;mapset enabled="yes"&gt;
    &lt;map type="binfile" enabled="yes" data="/home/jynus/osm/spain.navit.bin"/&gt;
&lt;/mapset&gt;</pre>
<p>¡Y ya está! Ejecutamos navit y estas son algunas de las capturas de pantalla que he obtenido navegando por Zaragoza:</p>
<div id="attachment_58" class="wp-caption aligncenter" style="width: 310px"><a href="http://jynus.com/blog/wp-content/uploads/2009/03/navit.png"><img class="size-medium wp-image-58" title="navit1" src="http://jynus.com/blog/wp-content/uploads/2009/03/navit-300x175.png" alt="De Tenor Fleta a Sagasta" width="300" height="175" /></a><p class="wp-caption-text">De Tenor Fleta a Sagasta</p></div>
<div id="attachment_59" class="wp-caption aligncenter" style="width: 310px"><a href="http://jynus.com/blog/wp-content/uploads/2009/03/navit2.png"><img class="size-medium wp-image-59" title="navit2" src="http://jynus.com/blog/wp-content/uploads/2009/03/navit2-300x175.png" alt="Navegando por la Plaza Paraíso" width="300" height="175" /></a><p class="wp-caption-text">Navegando por la Plaza Paraíso</p></div>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/03/navegando-con-gps-y-osm/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>&#8220;sudo&#8221; con una redirección de la salida</title>
		<link>http://jynus.com/blog/2009/03/sudo-con-una-redireccion-de-la-salida/</link>
		<comments>http://jynus.com/blog/2009/03/sudo-con-una-redireccion-de-la-salida/#comments</comments>
		<pubDate>Sun, 01 Mar 2009 13:43:54 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[Recetas]]></category>
		<category><![CDATA[archivo]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[echo]]></category>
		<category><![CDATA[redireccion]]></category>
		<category><![CDATA[salida]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[sudo]]></category>
		<category><![CDATA[tee]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=52</guid>
		<description><![CDATA[Si alguna vez habéis  intentado redireccionar la salida de un comando a un lugar que requiere privilegios de superusuario para escritura, os habréis topado con el siguiente error: $ sudo echo test &#62; /etc/deleteme bash: /etc/deleteme: Permiso denegado ¿Cómo? ¿Pero sudo no proporciona privilegios de superusuario cuando se ejecutan comandos? Sí, pero es necesario comprender [...]]]></description>
			<content:encoded><![CDATA[<p>Si alguna vez habéis  intentado redireccionar la salida de un comando a un lugar que requiere privilegios de superusuario para escritura, os habréis topado con el siguiente error:</p>
<pre>$ sudo echo test &gt; /etc/deleteme
bash: /etc/deleteme: Permiso denegado</pre>
<p>¿Cómo? ¿Pero sudo no proporciona privilegios de superusuario cuando se ejecutan comandos?</p>
<p>Sí, pero es necesario comprender bien el funcionamiento de una shell para ver lo que está pasando: sudo trata (salvo opciones) todo lo que viene a continuación como un comando; sin embargo es el programa intérprete de órdenes (bash o aquél que estemos ejecutando en ese momento) el que gestiona previamente las redirecciones de las salidas y entradas como <code>"&gt;"</code>, <code>"2&gt;"</code>, <code>"&lt;"</code> o <code>"|"</code>. Debemos, por tanto, evitar que nuestra shell las trate antes de ejecutar el sudo.</p>
<p>Por supuesto,  hacer un <code>sudo su[-c]</code>, nos libraría de estos problemas, pero no es nada elegante y le quita toda la razón de ser a utilizar sudo. Para evitarlo tenemos varias opciones:</p>
<pre>$ sudo sh -c 'echo "test" &gt; /etc/deleteme'
$ echo test | sudo tee /etc/deleteme
test</pre>
<p>(Éste último permite, adicionalmente, escribir al fichero y visualizar la salida simultáneamente).</p>
<p>¿Alguien sugiere más maneras?</p>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/03/sudo-con-una-redireccion-de-la-salida/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Resumen de la Mapping Party de Soria</title>
		<link>http://jynus.com/blog/2009/02/resumen-de-la-mapping-party-de-soria/</link>
		<comments>http://jynus.com/blog/2009/02/resumen-de-la-mapping-party-de-soria/#comments</comments>
		<pubDate>Tue, 24 Feb 2009 18:38:45 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[OSM]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=46</guid>
		<description><![CDATA[Acabo de llegar de casa después de pasar el fin de semana en Soria[*]. Se había realizado una quedada en esta localidad de los llamados “OSMeros” o “mappers” de España, es decir, los voluntarios que participamos en el proyecto colaborativo OpenStreetMap. ¿Qué es OpenStreetMap? Para los que no lo conozcan, el proyecto OSM es algo [...]]]></description>
			<content:encoded><![CDATA[<p style="margin-bottom: 0cm;">Acabo de llegar de casa después de pasar el fin de semana en Soria[*]. <a href="http://wiki.openstreetmap.org/wiki/Spanish_2nd_anniversary_mapping_party_(Soria)">Se había realizado una quedada en esta localidad</a> de los llamados “OSMeros” o “mappers” de España, es decir, los voluntarios que participamos en el proyecto colaborativo <a href="http://pulsar.unizar.es/osm/">OpenStreetMap</a>.</p>
<p style="margin-bottom: 0cm;">
<div id="attachment_47" class="wp-caption alignnone" style="width: 510px"><a href="http://blog.consta.de/2007/09/01/wir-basteln-uns-die-weltkarte/"><img class="size-full wp-image-47" title="OpenStreetMap cartoon" src="http://jynus.com/blog/wp-content/uploads/2009/02/openstreetmap_cartoon.jpg" alt="Dibujo inspirado en OSM. Creative Commons (¿?). Autor: Constantin Litvak" width="500" height="353" /></a><p class="wp-caption-text">Dibujo inspirado en OSM. Creative Commons (¿?). Autor: Constantin Litvak</p></div>
<p style="margin-bottom: 0cm;"><strong>¿Qué es OpenStreetMap?</strong></p>
<p style="margin-bottom: 0cm;">Para los que no lo conozcan, el proyecto OSM es algo así como la Wikipedia de los mapas. Se trata de una iniciativa colaborativa a través de internet para recoger, crear y difundir información geográfica libre. En España se empezó relativamente poco a generar mapas (los ingleses y alemanes nos llevan mucha ventaja), pero aún así puede echarse un vistazo a lo que se lleva ya hecho en la página <a href="http://www.openstreetmap.org/">www.openstreetmap.org</a>. Mucha gente se pregunta qué sentido tiene hacer algo que ya está disponible gratuitamente en Google Maps. La razón es que, pese a ser gratis, ese tipo de servicios ponen trabas a la forma en que puedes usar la información geográfica (no puedes, por ejemplo, modificar las imágenes para arreglar errores). Como se suele decir: <em>Free as in freedom, not as in free beer</em>.</p>
<p style="margin-bottom: 0cm;"><strong>¿Qué es lo que se pretendía hacer en Soria?</strong></p>
<p style="margin-bottom: 0cm;">El generar datos para crear el plano de todo el mundo (entre nosotros, informalmente, “mapear”) no es una actividad que se haga siempre en solitario. De vez en cuando, <a href="http://wiki.openstreetmap.org/wiki/Current_events">se queda en un determinado lugar para socializar</a>, y de paso completar un hueco que falte en los datos. Aprovechando que se celebran los dos años aproximadamente desde las primeras iniciativas de OSM en España, se decidió quedar en Soria y hacer una “mapping party” nacional (el año pasado fue en <a href="http://wiki.openstreetmap.org/wiki/Spanish_1st_anniversary_mapping_party_(Zaragoza)#More_to_come...">Zaragoza</a>).</p>
<p style="margin-bottom: 0cm;">Además, y como gran novedad, se ha aprovechado que hemos confluido algunos de los voluntarios más activos para crear una asociación (con el &#8220;original&#8221; nombre de “OpenStreetMap España”), federada de la fundación internacional que ya existe, con el ánimo de que se transforme en un ente oficial para ayudar al proyecto a conseguir sus objetivos (contactar con instituciones públicas, coordinación, etc.).</p>
<p style="margin-bottom: 0cm;"><strong>¿Qué es lo que finalmente se ha hecho?</strong></p>
<p style="margin-bottom: 0cm;">Se han firmado los estatutos y elegido los cargos (¡Iván for president! <img src='http://jynus.com/blog/wp-includes/images/smilies/icon_razz.gif' alt=':-P' class='wp-smiley' /> ), se han hablado de los próximos objetivos (¡trunks are red! <img src='http://jynus.com/blog/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> ). Se ha socializado alrededor de unas cuantas cervezas. E, incluso, ha habido tiempo para hacer un poco de mapping. Lo cierto es que <a href="http://openstreetmap.org/?lat=41.76378&amp;lon=-2.46852&amp;zoom=15&amp;layers=B000FTF">no había mucho que hacer en Soria</a>. Y es que desde que se propuso el lugar hasta esta semana muchos otros contribuidores ya habían dibujado buena parte de las calles y carreteras de la ciudad castellano-leonesa. La buena (y mala) noticia es que el mapa nunca está completo, por lo que siempre hay cosas que añadir y que mejorar en el mapa.</p>
<p style="margin-bottom: 0cm;">Celso ha escrito un <a href="http://lists.openstreetmap.org/pipermail/talk-es/2009-February/002602.html">resumen detallado de las actividades que llevamos a cabo durante estos tres días</a>.</p>
<p style="margin-bottom: 0cm;">Por último, animaros desde aquí, si estáis interesados, a conocer más cosas sobre el proyecto <a href="http://wiki.openstreetmap.org/">pasándoos por el wiki</a> o la <a href="http://lists.openstreetmap.org/listinfo/talk-es">lista de correo</a>.</p>
<p style="margin-bottom: 0cm;">[*] Desde que lo escribí hasta que lo publiqué, han pasado ya dos días</p>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/02/resumen-de-la-mapping-party-de-soria/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Ya soy Warper!</title>
		<link>http://jynus.com/blog/2009/02/ya-soy-warper/</link>
		<comments>http://jynus.com/blog/2009/02/ya-soy-warper/#comments</comments>
		<pubDate>Sat, 14 Feb 2009 18:14:51 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[Personal]]></category>
		<category><![CDATA[Warp]]></category>
		<category><![CDATA[formacion]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[trabajo]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=39</guid>
		<description><![CDATA[Empecé el martes, y ya se puede decir oficialmente que soy un Warper de pleno derecho. La empresa, creada inicialmente por varios ex-compañeros de la carrera, se dedica principalmente al desarrollo de proyectos de software relacionados con tecnologías &#8220;Open Source&#8221;. Actualmente ha crecido hasta convertirse en uno de los referentes aragoneses y españoles en nuevas [...]]]></description>
			<content:encoded><![CDATA[<div id="attachment_40" class="wp-caption alignright" style="width: 263px"><a href="http://warp.es"><img class="size-full wp-image-40" title="warp" src="http://jynus.com/blog/wp-content/uploads/2009/02/logo.png" alt="Logo de Warp Networks S.L." width="253" height="71" /></a><p class="wp-caption-text">Logo de Warp Networks S.L.</p></div>
<p>Empecé el martes, y ya se puede decir oficialmente que soy un <a href="http://warp.es">Warper</a> de pleno derecho. La empresa, creada inicialmente por varios ex-compañeros de la carrera, se dedica principalmente al desarrollo de proyectos de software relacionados con tecnologías &#8220;Open Source&#8221;. Actualmente ha crecido hasta convertirse en uno de los referentes aragoneses y españoles en nuevas tecnologías, teniendo como clientes administraciones públicas y empresas privadas tanto a nivel nacional como internacional.</p>
<p>En concreto, yo no trabajaré en el departamento de ingeniería, sino en el de <a href="http://warp.es/training/">formación</a>, como instructor MySQL. Y es que Warp Networks S.L. es <a href="http://solutions.mysql.com/solutions/partners/training?cc=ES&amp;level=&amp;find_partner=&amp;type=6&amp;special_type=">uno de los dos únicos partnes españoles de formación MySQL</a>.</p>
<p>De momento, la experiencia está siendo muy positiva, sobre todo por el buen rollo que se respira dentro de la oficina, principalmente por el trato con <a href="http://planet.warp.es/">mis compañeros</a>. He descubierto que la profesionalidad  con que se trabaja (y todos mis compañeros son grandes profesionales) no está reñida con un trato desenfadado.</p>
<p>Tú quizá todavía estés a tiempo de <a href="http://warp.es/blog/2009/01/27/we-are-hiring/">convertirte también en un Warper</a>&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/02/ya-soy-warper/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Creando &#8220;servicemenus&#8221; en KDE4</title>
		<link>http://jynus.com/blog/2009/01/creando-servicemenus-en-kde4/</link>
		<comments>http://jynus.com/blog/2009/01/creando-servicemenus-en-kde4/#comments</comments>
		<pubDate>Fri, 23 Jan 2009 19:16:38 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[Recetas]]></category>
		<category><![CDATA[convert]]></category>
		<category><![CDATA[dolphin]]></category>
		<category><![CDATA[escritorio]]></category>
		<category><![CDATA[kde]]></category>
		<category><![CDATA[kde4]]></category>
		<category><![CDATA[konqueror]]></category>
		<category><![CDATA[servicemenu]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=27</guid>
		<description><![CDATA[Supongamos que tenemos que realizar una tarea repetitiva varias veces en un escritorio Linux/KDE4. Para facilitarnos la vida, creamos un script en Bash para automatizar todo el proceso. Por ejemplo, el siguiente script automatiza, mediante la utilidad convert de ImageMagick, la transformación de una o varias imágenes al formato JPG y con un tamaño y [...]]]></description>
			<content:encoded><![CDATA[<p>Supongamos que tenemos que realizar una tarea repetitiva varias veces en un escritorio Linux/KDE4. Para facilitarnos la vida, creamos un script en <em>Bash</em> para automatizar todo el proceso. Por ejemplo, el siguiente script automatiza, mediante la utilidad <code>convert</code> de ImageMagick, la transformación de una o varias imágenes al formato JPG y con un tamaño y calidad dadas:</p>
<pre>jynus@clonmel2:~&gt; cat ~/bin/resize.sh
#!/bin/sh
SIZE=1280
QUALITY=90

for FILE in "$@"; do
   convert "$FILE" -quality $QUALITY -resize 1280x1280\&gt; \
"`dirname "$FILE"`/${SIZE}px-`basename "${FILE%.[^.]*}"`.jpg"
done</pre>
<p>Sin embargo, podemos facilitarnos la vida aún más añadiendo esta utilidad a los menús contextuales en Konqueror y Dolphin, que aparecen al pulsar con el botón derecho &gt; submenú acciones, para hacer más accesible la ejecución de ciertas tareas sobre determinados tipos de archivo. Para ello debemos crear una nueva entrada en los denominados &#8220;ServiceMenus&#8221;. Es relativamente fácil: para definir una nueva acción, tenemos que generar un archivo de extensión .desktop en el directorio <code>ServiceMenus</code> que se encuentrará en:</p>
<pre>/usr/share/kde4/services/ (para todos los usuarios)
~/.kde4/share/kde4/services/ (sólo para el usuario actual)</pre>
<p>El lugar exacto puede variar ligeramente entre distribuciones (ejecutar <code>kde4-config --path services</code> para averiguar el lugar exacto). Si dicho directorio no existe, lo cual ocurrirá el caso de utilizar el directorio de usuario, tendremos que crearlo nosotros mismos.<br />
Este archivo tendrá la siguiente pinta (se pueden definir más de una acción por archivo), que explicaré poco a poco:</p>
<pre>jynus@clonmel2:~&gt; cat /usr/share/kde4/services/ServiceMenus/resize.desktop
[Desktop Entry]
Encoding=UTF-8
ServiceTypes=KonqPopupMenu/Plugin,image/*
Type=Service
Actions=resizeImages;
TryExec=convert

[Desktop Action resizeImages]
Name=Redimensionar
Icon=image-jpeg
Exec=/home/jynus/bin/resize.sh %F</pre>
<p><em>&#8220;ServiceTypes=</em>&#8221; define sobre qué tipo de archivos vamos a poder realizar la acción (tipo mime). Por ejemplo, para vídeos sería <code>video/*</code>; para vídeos en formato avi, <code>video/x-msvideo</code>, etc. Para casos especiales como &#8220;todos los archivos UNIX&#8221; (incluido dispositivos, pipes, etc.), sería <code>all/all</code>, para los archivos &#8220;normales&#8221;, <code>all/allfiles</code> y para los directorios, <code>inode/directory</code>.<br />
&#8220;<em>Actions=<em>&#8221; </em></em>marca, separados por punto y coma, los nombres de las acciones que definiremos a continuación. En este caso sólo definiremos una, denominada <code>resizeImages</code><em><em>.<br />
</em></em>En la definición de la acción, le asignaremos un nombre (que será el que aparezca en el menú contextual), un icono ver los disponibles en <code>/usr/share/icons/default.kde4/*/actions/</code> y el comando que finalmente se ejecutará. En esta orden, podremos indicar mediante %f o %F, respectivamente, el archivo o conjunto de varios archivos (si el comando acepta varios parámetros) sobre los cuales se aplica esta función. En nuestro caso, tan sólo llamamos al script que mostrábamos antes para obtener el resultado final:</p>
<p><em><em></em></em></p>
<div id="attachment_30" class="wp-caption aligncenter" style="width: 718px"><em><em><em><em><img class="size-full wp-image-30" title="Un &quot;servicemenu&quot; añadido al menú contextual de Konqueror" src="http://jynus.com/blog/wp-content/uploads/2009/01/servicemenus.png" alt="Servicemenus" width="708" height="561" /></em></em></em></em><p class="wp-caption-text">Servicemenus</p></div>
<p><em><em></em></em></p>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/01/creando-servicemenus-en-kde4/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Grabar CDs y DVDs desde línea de comandos</title>
		<link>http://jynus.com/blog/2009/01/grabar-cds-y-dvds-desde-linea-de-comandos/</link>
		<comments>http://jynus.com/blog/2009/01/grabar-cds-y-dvds-desde-linea-de-comandos/#comments</comments>
		<pubDate>Tue, 20 Jan 2009 16:15:32 +0000</pubDate>
		<dc:creator>jynus</dc:creator>
				<category><![CDATA[Recetas]]></category>
		<category><![CDATA[cd]]></category>
		<category><![CDATA[cdda2wav]]></category>
		<category><![CDATA[cdrecord]]></category>
		<category><![CDATA[comandos]]></category>
		<category><![CDATA[consola]]></category>
		<category><![CDATA[dvd]]></category>
		<category><![CDATA[genisoimage]]></category>
		<category><![CDATA[icedax]]></category>
		<category><![CDATA[mkisofs]]></category>
		<category><![CDATA[wodim]]></category>

		<guid isPermaLink="false">http://jynus.com/blog/?p=6</guid>
		<description><![CDATA[Es posible que alguna vez necesitemos grabar un CD o DVD desde la línea de comandos. Por ejemplo, si alguna vez habéis estropeado irremediablemente el sistema de ficheros y sólo podéis iniciar sesión en modo consola, pero necesitáis grabar un CD de vuestra distro favorita (como me ocurrió a mí). O bien habéis ideado un [...]]]></description>
			<content:encoded><![CDATA[<p>Es posible que alguna vez necesitemos grabar un CD o DVD desde la línea de comandos. Por ejemplo, si alguna vez habéis estropeado irremediablemente el sistema de ficheros y sólo podéis iniciar sesión en modo consola, pero necesitáis grabar un CD de vuestra <em>distro</em> favorita (como me ocurrió a mí). O bien habéis ideado un sistema de copias de seguridad que automatiza cada cierto tiempo la grabación a medios ópticos, pero queréis evitar pesados programas gráficos.</p>
<p>A más de uno le sonará, aunque sólo sea por su aparición en los <em>logs</em> de programas gráficos como K3B o GnomeBaker, las utilidades <strong>cdrecord</strong> o <strong>growisofs</strong>, las cuales aparecen en la mayoría de guías similares a ésta. Lo que muchos quizá no sepáis es que estas utilidades <a href="http://linux.slashdot.org/article.pl?sid=06/09/04/1335226">han sido sustituidas por varios <em>forks</em></a>, aunque afortunadamente de parámetros similares y funcionalidades superiores (además, suele venir instalado paquetes de compatibilidad que crean enlaces simbólicos desde las antiguas nomenclaturas):</p>
<pre>jynus@clonmel2:~&gt; ls -la `which cdrecord`
lrwxrwxrwx 1 root root 5 dic 16 20:24 /usr/bin/cdrecord -&gt; wodim</pre>
<p>Empecemos: los paquetes necesarios, que suelen estar preinstalados por defecto por las distribuciones son:</p>
<pre>wodim genisoimage icedax</pre>
<p>Mediante el comando siguiente comando veremos cuáles son los dispositivos de grabación disponibles (olvidaros del anticuado -scanbus y la utilización de ids SCSI):</p>
<pre>jynus@clonmel2:~&gt; wodim --devices
wodim: Overview of accessible drives (1 found) :
-------------------------------------------------------------------------
 0  dev='/dev/scd0'     rwrw-- : 'HL-DT-ST' 'DVD-ROM GDRH20N'
-------------------------------------------------------------------------</pre>
<p><strong>Generar una imagen iso:</strong></p>
<pre>jynus@clonmel2:~&gt; genisoimage -l -R -J -o nombre_de_la_imagen.iso /ruta/a/grabar</pre>
<p>Con estos parámetros por defecto, seguirá enlaces simbólicos y generará extensiones Rockridge y Jouliet, lo cual permite relajar las restricciones restricciones en cuanto a nombres de archivo y directorios en sistemas que permiten nombres largos, como Windows y Linux.</p>
<p>Puede probarse la imagen generada antes de ser grabada haciendo:</p>
<pre>jynus@clonmel2:~&gt; sudo mount -o loop nombre_de_la_imagen.iso /mnt</pre>
<p><strong>Grabar la imagen generada:</strong></p>
<pre>jynus@clonmel2:~&gt; wodim -v -eject dev=/dev/scd0 nombre_de_la_imagen.iso</pre>
<p>Detectará automáticamente la velocidad más adecuada para grabar el CD, aunque podemos forzar una velocidad con el parámetro -speed=16, por ejemplo</p>
<p>Ambos comandos (generación de imagen y grabación de CD) pueden, por supuesto, concatenarse para hacer la grabación &#8220;al vuelo&#8221;):</p>
<pre>jynus@clonmel2:~&gt; genisoimage -l -R -J -o nombre_de_la_imagen.iso /ruta/a/grabar \
| wodim -v -eject dev=/dev/scd0 -
</pre>
<p><strong>Borrar un medio regrabable:</strong></p>
<pre>jynus@clonmel2:~&gt; wodim -v dev=/dev/scd0 blank=fast</pre>
<p><strong>Copiar un disco:</strong></p>
<pre>jynus@clonmel2:~&gt; dd if=/dev/cdrom of=nombre_de_la_imagen.iso
jynus@clonmel2:~&gt; wodim -v -eject dev=/dev/scd0 nombre_de_la_imagen.iso</pre>
<p><strong> Extraer las pistas y grabar un disco de audio:</strong></p>
<pre>jynus@clonmel2:~&gt; icedax -B -D /dev/cdrom -L 1
jynus@clonmel2:~&gt; wodim -v -eject dev=/dev/scd0 -audio audio_*.wav</pre>
]]></content:encoded>
			<wfw:commentRss>http://jynus.com/blog/2009/01/grabar-cds-y-dvds-desde-linea-de-comandos/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

