<?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>Digi3D en profundidad</title>
	<atom:link href="http://blogdigi3d.digi21.net/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://blogdigi3d.digi21.net</link>
	<description>Blog de desarrollo de Digi3D</description>
	<lastBuildDate>Wed, 25 Apr 2012 16:19:28 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>¿Latitud/Longitud o Longitud/Latitud? El caso de los Shapefiles</title>
		<link>http://blogdigi3d.digi21.net/?p=676</link>
		<comments>http://blogdigi3d.digi21.net/?p=676#comments</comments>
		<pubDate>Wed, 25 Apr 2012 12:17:33 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[DigiNG]]></category>
		<category><![CDATA[ArcGIS Explorer]]></category>
		<category><![CDATA[CRS]]></category>
		<category><![CDATA[epsg]]></category>
		<category><![CDATA[global mapper]]></category>
		<category><![CDATA[shapefile]]></category>
		<category><![CDATA[shapefiles]]></category>
		<category><![CDATA[sistemas de coordenadas de referencia]]></category>
		<category><![CDATA[WGS 84]]></category>
		<category><![CDATA[WGS84]]></category>

		<guid isPermaLink="false">http://blogdigi3d.digi21.net/?p=676</guid>
		<description><![CDATA[La verdad es que no me imaginaba la complejidad que tiene todo lo que tenga que ver con sistemas de coordenadas de referencia: a pesar de existir estándares, casi nadie los respeta. Un caso muy curioso es el de los Shapefiles. Los archivos Shapefile llevan asociado un archivo .prj que define la proyección cartográfica en [...]]]></description>
			<content:encoded><![CDATA[<p>La verdad es que no me imaginaba la complejidad que tiene todo lo que tenga que ver con sistemas de coordenadas de referencia: a pesar de existir estándares, casi nadie los respeta.</p>
<p>Un caso muy curioso es el de los <a title="Shapefiles" href="http://es.wikipedia.org/wiki/Shapefile" target="_blank">Shapefiles</a>. Los archivos <em>Shapefile</em> llevan asociado un archivo <em>.prj</em> que define la <a title="proyección cartográfica" href="http://es.wikipedia.org/wiki/Proyecci%C3%B3n_geogr%C3%A1fica" target="_blank">proyección cartográfica</a> en la que se almacenan las coordenadas de las entidades en el archivo.</p>
<p>El contenido de este archivo es una cadena <a title="Well Known Text" href="http://es.wikipedia.org/wiki/Well_Known_Text" target="_blank">Well Known Text</a>, que forma parte del estándar <a href="http://www.opengeospatial.org/standards/sfa" title="Simple Feature Access" target="_blank">Simple Feature Access</a> del <a href="http://www.opengeospatial.org/" title="Open Geospatial Consortium" target="_blank">Open Geospatial Consortium</a>.</p>
<p>Este estándar define que el orden en el que se almacenan las coordenadas lo define el sistema de coordenadas de referencia, luego si el sistema de coordenadas de referencia es un sistema de coordenadas geográfico con los ejes (Longitud, Latitud), las coordenadas a almacenar deben ir en el orden (Longitud, Latitud). Si por el contrario el sistema de coordenadas es también geográfico pero con los ejes (Latitud, Longitud), las coordenadas se deben almacenar en el orden (Latitud, Longitud).</p>
<p>Como el archivo <em>.prj</em> asociado a los <em>Shapefiles</em> es una cadena <em>Well Kown Text</em> todo hacía pensar que los programas que cargan archivos <em>Shapefile</em> y tienen asociado un sistema de coordenadas de referencia, deberían ser lo suficientemente inteligentes como para saber el orden de los ejes.</p>
<p><em>Digi3D 2011</em> cumple al 100% con el estándar. Además obtiene los parámetros de los sistemas de coordenadas de referencia de la base de datos <a href="http://www.epsg.org/Geodetic.html" title="EPSG Geodetic Parameter Dataset" target="_blank">EPSG Geodetic Parameter Dataset</a>, y en esta base de datos <strong>todos los sistemas de coordenadas geográficos</strong> tienen sus ejes ordenados de la forma <strong>(Latitud, Longitud)</strong>.</p>
<p>Por lo tanto, como <em>Digi3D 2011</em> es estricto cumpliendo el estándar, si seleccionamos como sistema de coordenadas de salida uno geográfico (basado en EPSG), el archivo generado tendrá las coordenadas ordenadas (Latitud, Longitud).</p>
<p>En teoría, y confiando en que todos los programas cumplieran con el estándar tal y como lo hace <em>Digi3D 2011</em>, decidí exportar el famoso modelo de <em>Bronchales</em> a <em>Shapefile</em> para cargarlo con <a href="http://www.esri.com/software/arcgis/explorer/index.html" title="ArcGIS Explorer Desktop" target="_blank">ArcGIS Explorer Desktop</a> y cual es mi sorpresa que el modelo aparece con los ejes cambiados y además en Kenia.</p>
<p><a href="http://blogdigi3d.digi21.net/wp-content/uploads/2012/04/ArcGisExplorerMostrandoBronchalesEnWGS84EnKenia.jpg"><img src="http://blogdigi3d.digi21.net/wp-content/uploads/2012/04/ArcGisExplorerMostrandoBronchalesEnWGS84EnKenia-300x163.jpg" alt="Captura de pantalla de ArcGIS Explorer mostrando el modelo de bronchales con los ejes cambiados y en Kenia" title="ArcGisExplorerMostrandoBronchalesEnWGS84EnKenia" width="300" height="163" class="alignnone size-medium wp-image-681" /></a></p>
<p>El contenido del archivo <em>.prj</em> del <em>Shapefile</em> cargado con <em>ArcGIS Explorer</em> en la captura de pantalla anterior es el siguiente:</p>
<p><code>GEOGCS["WGS 84",DATUM["World Geodetic System 1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree (supplier to define representation)",0.01745329251994328,AUTHORITY["EPSG","9122"]],AXIS["Lat", North],AXIS["Long", East],AUTHORITY["EPSG","4326"]]</code></p>
<p>que es una cadena <em>Well Known Text</em> en la que se comprueba que los parámetros se han obtenido de la base de datos <em>EPSG</em> (el sistema de coordenadas geográfico WGS 84 tiene asociado el número 4326).</p>
<p>Si te fijas en la cadena <em>Well Kown Text</em> se están definiendo los ejes del sistema geográfico, se indica que el primero es <em>Latitud</em> y que el segundo es <em>Longitud</em>.</p>
<p>De todos modos el estándar define que si una cadena <em>Well Kown Text</em> incluye una autoridad (<em>EPSG</em> en este caso), y el sofware que carga el archivo en cuestión implementa esa autoridad, debe hacer caso omiso a la información que aparece en la georeferenciación y crear el sistema de coordenadas basándose en el código (4326 en este caso). De forma que pensé: Quizás <em>ArcGIS explorer</em> trate de forma especial el sistema de coordenadas 4326, de modo que quité todas las referencias a la autoridad <em>EPSG</em> del archivo <em>.prj</em> creando lo siguiente:</p>
<p><code>GEOGCS["WGS 84",DATUM["World Geodetic System 1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree (supplier to define representation)",0.01745329251994328],AXIS["Lat",North],AXIS["Long",East]]</code></p>
<p>Pero desafortunadamente el resultado fue el mismo: modelo representado con los ejes cambiados y ubicado en Kenia.</p>
<p>Parece que que <em>ArcGIS Explorer</em> espera siempre (Longitud/Latitud) independientemente de lo que se indique en el sistema de coordenadas.</p>
<p>Por último, y ya para confirmar mis sospechas, cambié el sentido de los ejes para ver si afectaba en algo a la representación/ubicación con la siguiente cadena:</p>
<p><code>GEOGCS["WGS 84",DATUM["World Geodetic System 1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree (supplier to define representation)",0.01745329251994328],AXIS["Long",East], AXIS["Lat",North]]</code> </p>
<p>pero tampoco.</p>
<p>El siguiente conjunto de pruebas lo hice con <a href="http://www.bluemarblegeo.com/global-mapper/" title="Global Mapper" target="_blank">Global Mapper</a> con idénticos resultados: seleccionando como sistema de coordenadas de visualización <em>Geographic (Latitude/Longitude)</em> y con los tres archivos <em>.prj</em> asociados el resultado es el mismo.</p>
<p><a href="http://blogdigi3d.digi21.net/wp-content/uploads/2012/04/GlobalMapperMostrandoBronchalesMal.png"><img src="http://blogdigi3d.digi21.net/wp-content/uploads/2012/04/GlobalMapperMostrandoBronchalesMal-300x191.png" alt="" title="GlobalMapperMostrandoBronchalesMal" width="300" height="191" class="alignnone size-medium wp-image-704" /></a></p>
<p>Es posible que <em>Digi3D 2011</em> no estuviera generando bien el archivo <em>.prj</em>, así que decidí estropearlo, y al intentar cargarlo en <em>Global Mapper</em> este mostró un cuadro de diálogo indicando que el <em>Shapefile</em> no tiene información de proyección (por lo tanto si que se estaba generando bien, obviamente).</p>
<p>Si se acepta el cuadro de diálogo que indica que el <em>Shapefile</em> no tiene asociado un sistema de coordenadas, se muestra otro que nos permite seleccionar el sistema de coordenadas en el que están las coordenadas del archivo <em>Shapefile</em>, y ¡sorpresa! existe un botón titulado: <em>Init From EPSG&#8230;</em>. Genial, tan solo tenemos que pulsarlo y seleccionar el sistema de coordenadas 4326, que según EPSG, tiene los ejes ordenados de la forma (Latitud, Longitud) por lo tanto debe mostrar correctamente el modelo de <em>Bronchales</em>&#8230;</p>
<p><strong>Pues no</strong>. El resultado es el mismo, lo que me hizo pensar que nadie sigue el estándar (al menos en el caso de Shapefiles).</p>
<p>Investigando un poco, llegué a una <a href="http://social.msdn.microsoft.com/Forums/en/sqlspatial/thread/41250c42-25e6-4de7-953e-a6c41ada383f" target="_blank">discusión</a> muy interesante entre usuarios de <em>Microsoft SQL Server Spatial</em> y los ingenieros que han desarrollado ese producto (dentro de muy poco hablaré mucho sobre SQL Server espacial y Digi3D). Ahí se dice que <em>SQL Server Espacial</em> cumple al 100% con el estándar (igual que <em>Digi3D</em>) y que por lo tanto las coordenadas para el caso del sistema de coordenadas 4326 se almacenan como Latitud/Longitud. Un determinado usuario sin embargo me dió la pista sobre cómo se comportan el resto de programas:</p>
<blockquote><p>Coordinates in SHP (ESRI &#8220;shapefiles&#8221;), MIF (MapInfo) and other files produced by current GIS packages may be in many different coordinate systems. For lat/lon coordinate systems the order of coordinates is lon/lat in those systems.</p></blockquote>
<p>y un poco más adelante dice lo siguiente (lo que te hace pensar que realmente sabe del tema)</p>
<blockquote><p>Whew! &#8230; that&#8217;s what I mean by &#8220;universal&#8221; usage, as the above formats comprise, what? 99.99%? of the data out there.  If anyone can point to even a single repository of &#8220;Latitude / Longitude&#8221; data that uses (lat, lon) ordering instead of (lon, lat) ordering, I&#8217;d be very grateful to see the URL to that repository.</p></blockquote>
<p>Lo que significa que el resto de programas hacen caso omiso del estándar. En realidad me imagino que será porque existen archivos <em>Shapefile</em> en coordenadas geográficas desde mucho antes que este estándar. Piensa que el estándar es de 2006 (pero aún no he encontrado nadie que lo respete al 100% en lo referente a sistemas de coordenadas de referencia, quitando <em>Digi3D 2011</em>) y sin embargo el formato <em>Shapefile</em> data de 1998.</p>
<p>Aquí llegamos a un compromiso: <em>Digi3D 2011</em> cumple al 100% el estándar y creo personalmente que si existe un estándar es para cumplirlo, pero no puedes dar la espalda a lo que en teoría es el estándar <em>de facto</em>: toda esa cartografía que ya existe, que está almacenada en coordenadas geográficas y generada antes de que existiera el estándar, por programas que posiblemente no tengan ni idea de que existe un estándar, y que símplemente asocian un archivo <em>.prj</em> a cada <em>Shapefile</em> generado copiando una plantilla con el mismo nombre que el archivo <em>Shapefile</em> y con extensión <em>.prj</em>.</p>
<p>Así que la única solución que se me ha ocurrido ha sido permitir configurar en <em>Herramientas/Configuración/Importador Exportador de Shapefiles</em> una opción para indicar el orden de las coordenadas en caso de sistemas de coordenadas geográficos. Por defecto (y para seguir el estándar de facto, que no es el que se debería seguir) la opción es Longitud/Latitud. De esta manera podemos <em>saltarnos</em> el estándar en el caso de <em>Shapefiles</em>. También podrás encontrar estas opciones para <em>DWG</em> y <em>DGN</em>. </p>
<p>El resto de exportadores importantes <em>.bin, .bind, .kml, &#8230;</em> no permiten esta configuración pues los binarios de Digi3D (tanto <em>.bin</em> como <em>.bind</em>) cumplen al 100% con el estándar y los <em>.kml</em> tienen asociado de forma implícita el sistema de coordenadas geográfico <em>WGS 84</em> y además lo hace bien: primero se almacena la Latitud y luego la Longitud.</p>
Here is no comments yet by the time  your rss reader get this, Do you want to be the first commentor? Hurry up ]]></content:encoded>
			<wfw:commentRss>http://blogdigi3d.digi21.net/?feed=rss2&#038;p=676</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Actualizando la base de datos Epsg v7.9 para añadir transformaciones de cambio de datum ED50 -&gt; ETRS89 y ED50 -&gt; WGS84</title>
		<link>http://blogdigi3d.digi21.net/?p=664</link>
		<comments>http://blogdigi3d.digi21.net/?p=664#comments</comments>
		<pubDate>Tue, 17 Apr 2012 11:34:15 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Digi3D]]></category>
		<category><![CDATA[base de datos]]></category>
		<category><![CDATA[ED 50]]></category>
		<category><![CDATA[ED50]]></category>
		<category><![CDATA[epsg]]></category>
		<category><![CDATA[ETRS89]]></category>
		<category><![CDATA[OpenGis]]></category>
		<category><![CDATA[WGS 84]]></category>
		<category><![CDATA[WGS84]]></category>

		<guid isPermaLink="false">http://blogdigi3d.digi21.net/?p=664</guid>
		<description><![CDATA[Este post es avanzado, y supone que acabas de descargar una versión nueva de la base de datos EPSG y la has exportado al formato Microsoft SQL Server Compact tal y como se explica en el post Cómo se ha creado la base de datos geográfica EPSG en formato SQL Server Compact que se instala [...]]]></description>
			<content:encoded><![CDATA[<p>Este post es avanzado, y supone que acabas de descargar una versión nueva de la base de datos EPSG y la has exportado al formato Microsoft SQL Server Compact tal y como se explica en el post <a href="http://blogdigi3d.digi21.net/?p=635" title="Cómo se ha creado la base de datos geográfica EPSG en formato SQL Server Compact que se instala con Digi3D 2011">Cómo se ha creado la base de datos geográfica EPSG en formato SQL Server Compact que se instala con Digi3D 2011</a>.</p>
<p>La última versión de los archivos de rejilla de mínima curvatura NTv2 suministrados por el <a href="www.ign.es" title="Instituto Geográfico Nacional">Instituto Geográfico Nacional</a> data del 2009, sin embargo no existe ninguna transformación en la base de datos EPSG v7.9 (la mas moderna a día de hoy 17/04/2012) que utilice este archivo de mínima curvatura.</p>
<p>En este post te explico cómo modificar la base de datos para añadir una transformación que utilice el archivo de mínima curvatura actualizado.</p>
<p>La base de datos EPSG define 12 transformaciones para transformar entre los sistemas de coordenadas geográficos ED50 y ETRS89 y 40 transformaciones para transformar entre ED50 y WGS 84. </p>
<p>Puedes comprobarlo mediante las siguientes consultas SQL:</p>
<p>
<pre class="csharpcode">
<span class="kwrd">SELECT</span> <span class="kwrd">COUNT</span>(*)
<span class="kwrd">FROM</span> Coordinate_Operation
<span class="kwrd">WHERE</span> SOURCE_CRS_CODE=4230 <span class="kwrd">and</span> TARGET_CRS_CODE=4258 <span class="kwrd">and</span> COORD_OP_TYPE=<span class="str">'transformation'</span>;

<span class="kwrd">SELECT</span> <span class="kwrd">COUNT</span>(*)
<span class="kwrd">FROM</span> [Coordinate_Operation]
<span class="kwrd">WHERE</span> SOURCE_CRS_CODE=4230 <span class="kwrd">and</span> TARGET_CRS_CODE=4326 <span class="kwrd">AND</span> COORD_OP_TYPE=<span class="str">'transformation'</span>;
</pre>
</p>
<p>Muchas de ellas consisten simplemente en desplazamientos geográficos (es decir, transformar de coordenadas geográficas a geocéntricas y realizar un desplazamiento en geocéntricas para luego volver a transformar a geográficas), por lo que tienen poca precisión.</p>
<p>De entre todos los algoritmos para transformar entre sistemas geográficos, el más preciso es el método <em>interpolación mediante una rejilla de mínima curvatura</em>. Esta rejilla le indica al programa: “Para este par de coordenadas latitud, longitud en el sistema de coordenadas de referencia ED50, le corresponde este otro par de coordenadas latitud, longitud en el sistema de coordenadas de referencia ETRS89”. Luego el programa tan solo tiene que interpolar mediante una interpolación bilineal. El resultado es muy preciso (20 cm para el caso de España).</p>
<p>Es responsabilidad de cada país/organismo crear su archivo de rejilla y de publicarlo.  Cualquiera podría inventarse su propio formato de rejilla (lo que obligaría a que los desarrolladores de programas de cartografía y sistemas de información geográfica tuvieran que implementar un importador específico para cada formato de rejilla) pero existe uno estandarizado denominado NTv2.</p>
<p>En el caso de España, el organismo encargado de crear y actualizar ese archivo de rejilla es el Instituto Geográfico Nacional que publica este archivo a través de su página en internet: <a href="www.ign.es" title="Instituto Geográfico Nacional">www.ign.es</a>.</p>
<p>Descarga los archivos de rejilla entrando en <a href="www.ign.es" title="Instituto Geográfico Nacional">www.ign.es</a>: en el menú Herramientas de la página web, puedes seleccionar la opción: Rejilla para cambio de Datum entre ED50 y ETRS89 (en formato NTV2). Verás que puedes descargar dos rejillas: una para Península y otra para Baleares. Descarga el archivo Península, comprobarás que el nombre del archivo descargado es PENR2009.gsb.</p>
<p>Como la creación y mantenimiento de los archivos NTv2 depende del centro geográfico nacional de cada país, la base de datos EPSG no distribuye estos datos, sin embargo, <em>Digi3D 2011</em> incorpora una copia de estos archivos en la carpeta %ProgramData%\Digi3D 2011\OpenGis, de modo que en ese directorio encontrarás el archivo PENR2009.gsb.</p>
<p>El único inconveniente es que la base de datos original EPSG (la que acabas de descargar de <a href="http://www.epsg.org/Geodetic.html">http://www.epsg.org/Geodetic.html</a> no dispone de ninguna transformación que utilice este archivo, únicamente tiene transformaciones que utilizan versiones anteriores de ese archivo.</p>
<p>Vamos a comprobarlo:</p>
<p>La base de datos EPSG dispone de una tabla denominada &#8216;Coordinate_Operation Method&#8217; que registra los códigos, nombres y descripciones de las distintas transformaciones. </p>
<p>Consultemos el código asociado al algoritmo NTv2 mediante la siguiente consulta SQL:</p>
<p>
<pre class="csharpcode">
<span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> [Coordinate_Operation Method]
<span class="kwrd">WHERE</span> COORD_OP_METHOD_NAME=<span class="str">'NTv2'</span>;</pre>
</p>
<p>Puedes comprobar que el algoritmo NTv2 tiene asociado el código 9615.</p>
<p>Podemos consultar también los parámetros que recibe un determinado algoritmo. Comprobemos los parámetros que recibe el algoritmo 9615:</p>
<p>La base de datos EPSG dispone de una tabla &#8216;Coordinate_Operation Parameter Usage&#8217; que nos indica los parámetros de cada algoritmo. Estos parámetros (como todo en la base de datos EPSG) tienen asignado un código. Disponemos de otra tabla &#8216;Coordinate_Operation Parameter&#8217; que nos muestra una descripción de cada tipo de parámetro.</p>
<p>Para averiguar los parámetros que recibe la transformación con código 9615 podemos ejecutar la siguiente consulta SQL:</p>
<p>
<pre class="csharpcode">
<span class="kwrd">SELECT</span> *
<span class="kwrd">FROM</span> [Coordinate_Operation <span class="kwrd">Parameter</span> <span class="kwrd">Usage</span>]
<span class="kwrd">WHERE</span> COORD_OP_METHOD_CODE=9615;
</pre>
</p>
<p>Eso nos devolverá un único registro indicando que el único parámetro que recibe el algoritmo 9615 es el parámetro cuyo código es el 8656.</p>
<p>Podemos consultar el significado de ese parámetro con la siguiente consulta SQL:</p>
<p>
<pre class="csharpcode">
<span class="kwrd">SELECT</span> *
<span class="kwrd">FROM</span> [Coordinate_Operation <span class="kwrd">Parameter</span>]
<span class="kwrd">WHERE</span> PARAMETER_CODE=8656;</pre>
</p>
<p>Por último, si quieres hacerlo bien, puedes hacerlo con una consulta JOIN de la siguiente manera:</p>
<p>
<pre class="csharpcode">
<span class="kwrd">SELECT</span> *
<span class="kwrd">FROM</span> [Coordinate_Operation <span class="kwrd">Parameter</span> <span class="kwrd">Usage</span>] <span class="kwrd">AS</span> A
<span class="kwrd">JOIN</span> [Coordinate_Operation <span class="kwrd">Parameter</span>] <span class="kwrd">AS</span> B
<span class="kwrd">ON</span> A.PARAMETER_CODE = B.PARAMETER_CODE
<span class="kwrd">WHERE</span> A.COORD_OP_METHOD_CODE=9615;</pre>
</p>
<p>De entre todas las posibles transformaciones (te recuerdo que había 12) entre ED50 y ETRS89 que define la base de datos EPSG, únicamente hay dos que utilicen el algoritmo 9615: La transformación 15895: <em>ED50 to ETRS89 (11)</em> y la operación 15932: <em>ED50 to ETRS89 (12)</em>.</p>
<p>Puedes comprobarlo mediante la siguiente consulta SQL:</p>
<p>
<pre class="csharpcode">
<span class="kwrd">SELECT</span> *
<span class="kwrd">FROM</span> Coordinate_Operation
<span class="kwrd">WHERE</span> SOURCE_CRS_CODE=4230 <span class="kwrd">and</span> TARGET_CRS_CODE=4258 <span class="kwrd">and</span> COORD_OP_TYPE=<span class="str">'transformation'</span> <span class="kwrd">AND</span> COORD_OP_METHOD_CODE=9615;
</pre>
</p>
<p>Si nos leemos los comentarios de estas dos transformaciones, rápidamente averiguamos que la transformación 15895 ha sido sustituida por la 15932, de modo que la que nos interesará utilizar es la 15932, pero tal y como vamos a comprobar muy pronto, esta transformación no nos sirve, pues el archivo de rejilla a que utiliza esta transformación no es “PENR2009.gsb”.</p>
<p>Los parámetros de las transformaciones se almacenan en la tabla &#8216;Coordinate_Operation Parameter Value&#8217;.</p>
<p>Esta tabla almacena los parámetros de todos los algoritmos utilizados en todas las posibles transformaciones especificadas en la base de datos EPSG. Actualmente tiene 15743 registros.</p>
<p>Cada registro tiene varios campos:</p>
<table>
<tr>
<td>COORD_OP_CODE</td>
<td>COORD_OP_METHOD_CODE</td>
<td>PARAMETER_CODE</td>
<td>PARAMETER_VALUE</td>
<td>PARAM_VALUE_FILE_REF</td>
<td>UOM_CODE</td>
</tr>
</table>
<p>Si comprobamos los registros de esta tabla para el código de operación 15932, comprobaremos que el nombre del archivo de rejilla asignado para esa operación es “SPED2ETV2.gsb”.</p>
<p>
<pre class="csharpcode">
<span class="kwrd">SELECT</span> * <span class="kwrd">FROM</span> [Coordinate_Operation <span class="kwrd">Parameter</span> <span class="kwrd">Value</span>] <span class="kwrd">WHERE</span> COORD_OP_CODE=15932;
</pre>
</p>
<p>Por lo tanto esta operación no nos sirve. El archivo “SPED2ETV2.gsb” es el nombre que tenía la versión antigua de los archivos de rejilla, de hecho, la operación 15932 se añadió a la base de datos EPSG con fecha 27-03-2007, y el nombre de archivo “PENR2009” nos hace pensar que este archivo se creó en el año 2009.</p>
<p>Podrías pensar que una solución sería modificar este registro de la base de datos y cambiar la palabra “SPED2ETV2.gsb” por “PENR2009.gsb”, pero esto <strong>no se puede hacer porque la base de datos EPSG es inmutable</strong>. No se permiten cambios. Podemos añadir información, transformaciones, sistemas de coordenadas, pero nunca cambiar nada.</p>
<p>Así que no nos queda más remedio que crear una transformación nueva.</p>
<p>En la documentación de EPSG te indican que si quieres añadir información, utilices siempre códigos con valores superiores a 32767, por lo tanto vamos a crear una transformación con código 32768 que nos servirá para transformar entre los sistemas de coordenadas geográficos ED50 (4230) y ETRS89 (4326), para la zona de España con el algoritmo 9615 y utilizando como archivo de rejilla NTv2 el archivo “PENR2009.gsb”.</p>
<p>Para ello ejecutaremos la consulta:</p>
<p>
<pre class="csharpcode">
<span class="kwrd">INSERT</span> <span class="kwrd">INTO</span> Coordinate_Operation (COORD_OP_CODE, COORD_OP_NAME, COORD_OP_TYPE, SOURCE_CRS_CODE, TARGET_CRS_CODE, COORD_TFM_VERSION, COORD_OP_VARIANT, AREA_OF_USE_CODE, COORD_OP_SCOPE, COORD_OP_ACCURACY, COORD_OP_METHOD_CODE, REMARKS, INFORMATION_SOURCE, DATA_SOURCE, REVISION_DATE, SHOW_OPERATION, DEPRECATED)
<span class="kwrd">VALUES</span> (32768, <span class="str">'ED50 a ETRS89 (14)'</span>, <span class="str">'transformation'</span>, 4230, 4258, <span class="str">'IGN-Esp v3'</span>, 14, 3429, <span class="str">'For applications to an accuracy of 10-15cm (95% confidence) for Spain mainland and about 4cm (95%) for Balearic Islands.'</span>,0.2,9615,<span class="str">'Reemplaza a ED50 to ETRS89(12)'</span>,<span class="str">'Instituto Geográfico Nacional, www.ign.es'</span>, <span class="str">'OGP'</span>, 2009-01-01, 1, 0);
</pre>
</p>
<p>Y ya puestos, y considerando que el sistema de coordenadas geográfico ETRS89 es idéntico al WGS84, vamos a crear una transformación para transformar entre los sistemas de coordenadas geográficos ED50 (4230) y WGS84 (4326), esta vez con código 32769 con la siguiente consulta:</p>
<p>
<pre class="csharpcode">
<span class="kwrd">INSERT</span> <span class="kwrd">INTO</span> Coordinate_Operation (COORD_OP_CODE, COORD_OP_NAME, COORD_OP_TYPE, SOURCE_CRS_CODE, TARGET_CRS_CODE, COORD_TFM_VERSION, COORD_OP_VARIANT, AREA_OF_USE_CODE, COORD_OP_SCOPE, COORD_OP_ACCURACY, COORD_OP_METHOD_CODE, REMARKS, INFORMATION_SOURCE, DATA_SOURCE, REVISION_DATE, SHOW_OPERATION, DEPRECATED)
<span class="kwrd">VALUES</span> (32769, <span class="str">'ED50 a WGS84 (43)'</span>, <span class="str">'transformation'</span>, 4230, 4326, <span class="str">'IGN-Esp v3'</span>, 43, 3429, <span class="str">'For applications to an accuracy of 10-15cm (95% confidence) for Spain mainland and about 4cm (95%) for Balearic Islands.'</span>,0.2,9615,<span class="str">'Valor de los parámetros de la transformación ED50 a ETRS89 (14). Asume que ETRS89 y WGS 84 se consideran iguales dentro de la precisión de la transformación. Reemplaza ED50 to WGS 84 (41)'</span>,<span class="str">'Instituto Geográfico Nacional, www.ign.es'</span>, <span class="str">'OGP'</span>, 2009-01-01, 1, 0);
</pre>
</p>
<p>Tan solo nos queda añadir en la tabla &#8216;Coordinate_Operation Parameter Value&#8217; los parámetros para estas dos transformaciones.</p>
<p>Para ello tan solo tenemos que ejecutar las consultas:</p>
<p>
<pre class="csharpcode">
<span class="kwrd">INSERT</span> <span class="kwrd">INTO</span> [Coordinate_Operation <span class="kwrd">Parameter</span> <span class="kwrd">Value</span>]
<span class="kwrd">VALUES</span> (32768, 9615, 8656, <span class="kwrd">NULL</span>, <span class="str">'PENR2009.gsb'</span>, <span class="kwrd">NULL</span>);

<span class="kwrd">INSERT</span> <span class="kwrd">INTO</span> [Coordinate_Operation <span class="kwrd">Parameter</span> <span class="kwrd">Value</span>]
<span class="kwrd">VALUES</span> (32769, 9615, 8656, <span class="kwrd">NULL</span>, <span class="str">'PENR2009.gsb'</span>, <span class="kwrd">NULL</span>);
</pre>
</p>
<p>y ya lo tenemos. Ahora cuando Digi3D detecte que tiene que transformar entre los sistemas de coordenadas de referencia ED50 y ETRS89 o ED50 y WGS 84 nos mostrará esta transformacion en el cuadro de diálogo de posibles transformaciones.</p>
Here is no comments yet by the time  your rss reader get this, Do you want to be the first commentor? Hurry up ]]></content:encoded>
			<wfw:commentRss>http://blogdigi3d.digi21.net/?feed=rss2&#038;p=664</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Cómo se ha creado la base de datos geográfica EPSG en formato SQL Server Compact que se instala con Digi3D 2011</title>
		<link>http://blogdigi3d.digi21.net/?p=635</link>
		<comments>http://blogdigi3d.digi21.net/?p=635#comments</comments>
		<pubDate>Mon, 23 Jan 2012 19:52:57 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Digi3D]]></category>
		<category><![CDATA[base de datos]]></category>
		<category><![CDATA[epsg]]></category>

		<guid isPermaLink="false">http://blogdigi3d.digi21.net/?p=635</guid>
		<description><![CDATA[Estoy terminando de implementar los sistemas de cordenadas de referencia en Digi3D 2011, y me he encontrado con un problema inesperado: Las consultas SQL que debe ejecutar Digi3D 2011 para realizar determinadas consultas sobre la base de datos geográfica varían en función de si la base de datos está almacenada en un archivo .mdb (de [...]]]></description>
			<content:encoded><![CDATA[<p>Estoy terminando de implementar los sistemas de cordenadas de referencia en <em>Digi3D 2011</em>, y me he encontrado con un problema inesperado: Las consultas SQL que debe ejecutar Digi3D 2011 para realizar determinadas consultas sobre la base de datos geográfica varían en función de si la base de datos está almacenada en un archivo .mdb (de <em>Microsoft Access</em>) o está alojada en una instancia de <em>Microsoft SQL Server</em>.</p>
<p>La base de datos geográfica en formato .mdb se puede utilizar en las versiones de 32 bits de Digi3D 2011 sin ningún problema. Las versiones de 64 bits no tienen esa suerte, ya que no es posible que un programa de 64 bits se conecte con un archivo en formato .mdb.</p>
<p>La única manera que tenían las versiones de 64 bits de conectarse con esa base de datos era creando una base de datos SQL Server y configurar el programa para que se conecte con esta base de datos en vez de con el archivo local .mdb. Tienes instrucciones de cómo realizar esa tarea en el post <a href="http://blogdigi3d.digi21.net/?p=189" title="Base de datos EPSG en Digi3D 2011 edición de 64 Bits">Base de datos EPSG en Digi3D 2011 edición de 64 Bits</a>.</p>
<p>El problema ha surgido porque con la versión que liberaré esta semana (me imagino que el día 25 a 27 de enero de 2012) en ciertas consultas es necesario saber si el usuario ha marcado alguna operación como visible o no. </p>
<p>Si en un determinado momento Digi3D 2011 necesita transformar una coordenada de un sistema de coordenadas de referencia a otro, tiene que realizar una consulta SQL sobre la base de datos geográfica y ésta le indica de todas las operaciones (cada una con mejor o peor precisión) que existen para transformar coordenadas entre los dos sistemas.</p>
<p>Si se localiza más de una operación, Digi3D 2011 muestra un cuadro de diálogo al usuario indicando que se han localizado múltiples métodos, invitándole a que seleccione el más apropiado.</p>
<p>En ocasiones no queremos que Digi3D 2011 muestre todas las posibles opciones, como por ejemplo en el caso de transformar entre los sistemas de coordenadas de referencia ED50 y ETRS89: siempre nos interesará utilizar la operación que utiliza el algoritmo NTv2 y nunca las que utilizan desplazamientos geocéntricos.</p>
<p>La base de datos dispone de un campo denominado <em>SHOW_OPERATION</em> que en <em>Access</em> es de tipo booleano, pero que en SQL Server se convierte a campo de tipo bit.</p>
<p>Y el problema surgue aquí: Si queremos localizar todas las transformaciones entre los sistemas de coordenadas 4230 y 4258 que sean visibles, con SQL Server tenemos que ejecutar la siguiente consulta:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<p>
<pre class="csharpcode">
<span class="kwrd">SELECT</span> *
<span class="kwrd">FROM</span> Coordinate_Operation
<span class="kwrd">WHERE</span> SOURCE_CRS_CODE=4230
<span class="kwrd">AND</span> TARGET_CRS_CODE=4258
<span class="kwrd">AND</span> COORD_OP_TYPE=<span class="str">'transformation'</span>
<span class="kwrd">AND</span> SHOW_OPERATION=1
</pre>
</p>
<p>Sin embargo, si la base de datos no está ubicada en un SQL Server, sino que estamos conectados con el .mdb la consulta se realiza de esta otra forma:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<p>
<pre class="csharpcode">
<span class="kwrd">SELECT</span> *
<span class="kwrd">FROM</span> Coordinate_Operation
<span class="kwrd">WHERE</span> SOURCE_CRS_CODE=4230
<span class="kwrd">AND</span> TARGET_CRS_CODE=4258
<span class="kwrd">AND</span> COORD_OP_TYPE=<span class="str">'transformation'</span>
<span class="kwrd">AND</span> SHOW_OPERATION=<span class="kwrd">True</span>
</pre>
</p>
<p><o>Si te fijas hay una pequeña diferencia: en SQL Server utilizamos un <em>1</em> para filtrar los registros que tienen el campo <em>SHOW_OPERATION</em> a activo, sin embargo con Access tenemos que utilizar un <em>True</em>.</p>
<p>La solución más obvia es olvidarme del archivo .mdb que está desfasado (fíjate cómo de desfasado está que los señores de Microsoft directamente no te dejan conectarte con él en las versiones de 64 bits), pero eso obligaría a instalar un SQL Server Express junto con cada Digi3D 2011. Hasta ahora, así ha sido para las versiones de 64 bits de Digi3D 2011: había que instalar un SQL Server Express (o mejor) en el equipo o en la red de la empresa para que Digi3D 2011 se pudiera conectar con la base de datos geográfica.</p>
<p>Como no quiero forzar a instalar un SQL Server en cada equipo donde se instale Digi3D 2011 (porque habrá entornos en los que sencillamente no se puede, como centros geográficos nacionales con mucha seguridad), no queda más remedio que probar una tercera opción: Convertir la base de datos EPSG a formato Microsoft SQL Server Compact Edition, que es digámoslo así el sustituto de los obsoletos .mdb.</p>
<p>Este formato es un SQL Server en un archivo (con extensión .sdf) y está pensado para mono usuario y tiene la ventaja de que no hay que instalar un servidor, con copiar un par de DLLs junto con la instalación de Digi3D 2011 sobra.</p>
<p>El problema es que no existe ningún asistente, importador o exportador en <em>Microsoft SQL Server Management Studio</em> que convierta o un <em>.mdb</em> o una base de datos <em>.mdf</em> (las de <em>Microsoft SQL Server</em>) en un archivo .sdf.</p>
<p>Se me ocurren tres soluciones: </p>
<ol>
<li>Crear un <em>guión</em> (pulsando con el botón derecho del ratón en la base de datos EPSG en el panel <em>Object Explorer</em> de <em>Microsoft SQL Server Management Studio</em>, y seleccionando la opción <em>Tasks/Generate scripts&#8230;</em> del menú contextual. Crear una nueva base de datos en formato <em>Microsoft SQL Server Compact</em> y ejecutar sobre esta nueva base de datos el guión.</li>
<li>Localizar alguna herramienta por Internet que convierta una base de datos .mdf (la extensión que tienen las bases de datos <em>Microsoft SQL Server</em>) en una con extensión .sdf.</li>
<li>Programar una utilidad que realice la tarea.</li>
</ol>
<p>La primera solución es compleja, pues el SQL que se genera en el guión no es compatible con <em>SQL Server Compact</em> y habría que corregirlo: añadir por puntos y comas al final de cada entrada, eliminar las cláusulas <em>GO</em>, el comando <em>print</em> que aparece en el guión cada X líneas, &#8230;).</p>
<p>La segunda opción es mucho más sencilla siempre y cuando exista alguna utilidad por internet.</p>
<p>La tercera es la última opción, pues debe de existir alguna utilidad por internet, ¡y la hay!, si buscas en Google localizarás un programa denominado <a href="http://flyhoward.com/SDF_Viewer.aspx" title="SDF Viewer" target="_blank">SDF Viewer</a> que nos va a solucionar la papeleta. Esta herramienta es de pago, pero permite que la utilicemos 15 días (tiempo más que de sobra para realizar esta práctica).</p>
<p>Vamos a ver cómo he construido la base de datos con extensión .sdf que se copia junto con la instalación de Digi3D 2011 (en la carpeta %ProgramData%\Digi3D 2011\epsg.sdf).</p>
<ol>
<li>Descárga e instala la aplicación <em>SDF Viewer</em>.</li>
<li>Ejecuta la aplicación <em>SDF Viewer</em>.</li>
<li>Aparecerá un cuadro de diálogo indicando que quedan X días de prueba. Pulsa la opción <em>Ask Later</em>.</li>
<li>En <em>Tools</em> pulsa la opción <em>From SQL Server</em>.</li>
<li>En el cuadro de diálogo <em>Import From SQL Server</em>, en el campo <em>Server name:</em> teclea la dirección de tu servidor <em>Microsoft SQL Server</em> en el cual tienes la base de datos EPSG (aquella que importaste si seguiste los pasos del post <a href="http://blogdigi3d.digi21.net/?p=189" title="Base de datos EPSG en Digi3D 2011 edición de 64 Bits">Base de datos EPSG en Digi3D 2011 edición de 64 Bits</a>). Usualmente aquí tienes que poner <em>.\sqlexpress</em></li>
<li>En <em>Authentication:</em> selecciona el método de autenticación de tu instancia de SQL Server<em>. Por defecto es </em><em>Windows Authentication</em>.</li>
<li>Pulsa el botón <em>Connect</em>.</li>
<li>En <em>Database</em> selecciona la base de datos <em>EPSG</em>.</li>
<li>En <em>Tables</em> pulsa el botón <em>Select All</em>.</li>
<li>Ásegúrate de que está seleccionado el <em>checkbox</em> titulado <em>Include Data</em>.</li>
<li>Pulsa el botón <em>Script</em>.</li>
<li>En el cuadro de diálogo común <em>Guardar Como</em> indica la ubicación al archivo .sqlce que va a generar el programa <em>SDF Viewer</em> con el SQL necesario para crear las tablas y los datos.</li>
<li>Cierra el programa SDF Viewer.</li>
</ol>
<p>Bien, ahora vamos a crear una base de datos nueva en formato <em>Microsoft SQL Server Compact</em> y la rellenaremos con la información generara con el programa <em>SDF Viewer</em>.</p>
<ol>
<li>Ejecuta <em>Microsoft SQL Server Management Studio</em>.</li>
<li>En el cuadro de diálogo <em>Connect to Server</em>, en el campo <em>Server type</em>, selecciona <em>SQL Server Compact</em>.</li>
<li>En el campo <em>Database file</em> despliega la lista y selecciona <em>&lt;New database&gt;</em>.</li>
<li>En el cuadro de diálogo <em>Create New SQL Server Compact Database</em>, selecciona la ruta donde crear la base de datos <em>SQL Server Compact</em> y pulsa OK.</li>
<li>Aparecerá un cuadro de diálogo indicando que si estás seguro de crear la base de datos sin protección por contraseña. Confirma pulsando <em>Yes</em>.</li>
<li>Ya está creada la base de datos, pero volverá a aparecer el cuadro de diálogo <em>Create New SQL Server Compact Database</em>. Pulsa <em>Cancel</em>.</li>
<li>Ahora aparecerá el cuadro de diálogo <em>Connect to Server</em> con la ruta a la base de datos que acabas de crear. Pulsa el botón <em>Connect</em> para conectarte a ella.</li>
<li>Arrastra y suelta el archivo <em>.sqlce</em> que creaste en la sección anterior.</li>
<li>Pulsa el botón <em>Execute</em>.
</li>
</ol>
<p>Ya tienes creada la base de datos <em>EPSG</em> en formato <em>Microsoft SQL Server Compact</em>.<br />
</o></p>
<p>El instalador no solo copia esta base de datos en %ProgramData%\Digi3D 2011\EPSG, sino que además almacena en el registro la ubicación de esta base de datos.</p>
<p>En función de si la versión de Digi3D 2011 es de 32 o de 64 bits la ubicación de la ruta a la base de datos se almacenará en una u otra entradas del registo.<br />
A continuación tienes una tabla que indica dónde se almacena en el registro la cadena de conexión a la base de datos geográfica.</p>
<table border="1">
<tr>
<td>Digi3D 2011 edición de 32 bits</td>
<td>Digi3D 2011 edición de 64 bits</td>
</tr>
<tr>
<td>
HKEY_LOCAL_MACHINE\SOFTWARE\Digi21\Digi3D2011\App\Configuration\EPSGConnectionString
</td>
<td>
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Digi21\Digi3D2011\App\Configuration\EPSGConnectionString
</td>
</tr>
</table>
Here is no comments yet by the time  your rss reader get this, Do you want to be the first commentor? Hurry up ]]></content:encoded>
			<wfw:commentRss>http://blogdigi3d.digi21.net/?feed=rss2&#038;p=635</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Imprimir la configuración de teclado</title>
		<link>http://blogdigi3d.digi21.net/?p=600</link>
		<comments>http://blogdigi3d.digi21.net/?p=600#comments</comments>
		<pubDate>Wed, 21 Sep 2011 11:49:51 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[DigiNG]]></category>
		<category><![CDATA[teclas.mnu]]></category>

		<guid isPermaLink="false">http://blogdigi3d.digi21.net/?p=600</guid>
		<description><![CDATA[Como ya sabes, el formato del archivo de teclas Teclas.mnu ha cambiado en la versión 2011. El formato antiguo tenía una serie de limitaciones como era que únicamente permitía añadir una orden a una pulsación de tecla (lo que te obligaba a crear archivos de macroinstrucciones externos si querías asignar más de una orden a [...]]]></description>
			<content:encoded><![CDATA[<p>Como ya sabes, el formato del archivo de teclas <em>Teclas.mnu</em> ha cambiado en la versión 2011.</p>
<p>El formato antiguo tenía una serie de limitaciones como era que únicamente permitía añadir una orden a una pulsación de tecla (lo que te obligaba a crear archivos de macroinstrucciones externos si querías asignar más de una orden a una pulsación de tecla, cosa que personalmente nunca me ha gustado) y peor aún, no había forma saber a que tecla o combinación de teclas se refería un determinado comando, ya que cada pulsación de tecla o combinación de teclas generaba un número único imposible de identificar. Recuerda que existe un post en este blog que te explica cómo convertir un archivo de teclas antiguo al nuevo formato <a href="http://blogdigi3d.digi21.net/?p=192" title="http://blogdigi3d.digi21.net/?p=192">http://blogdigi3d.digi21.net/?p=192</a></p>
<p>En este post vamos a aprender cómo convertir un archivo de teclas en formato xml en algo que sea legible por el ser humano, la idea es modificar el archivo xml para que al hacer doble clic sobre él, el navegador/explorador web que utilices nos muestre una tabla con la tecla, indicando además si estaba pulsada la tecla Alt, Mayúsculas o Control y la descripción asociada a la pulsación de tecla.</p>
<p>La idea es convertir esto:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">&lt;?</span><span class="html">xml</span> <span class="attr">version</span><span class="kwrd">="1.0"</span> <span class="attr">encoding</span><span class="kwrd">="utf-8"</span>?<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">Keyboard</span> <span class="attr">xmlns</span><span class="kwrd">="http://schemas.digi21.net/Digi3D/keyboard/v1.0"</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="NUMERO 0"</span> <span class="attr">Description</span><span class="kwrd">="Cambia el tamaño del cursor"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[CURSOR]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="NUMERO 1"</span> <span class="attr">Description</span><span class="kwrd">="Borra entidades por ventana"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[BORRA_V]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="TECLA DE ADICION"</span> <span class="attr">Description</span><span class="kwrd">="Selecciona el código activo"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[COD]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="NUMERO 9"</span> <span class="attr">Description</span><span class="kwrd">="Dibuja un cruce de caminos"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[CRUCE]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="NUMERO 6"</span> <span class="attr">Description</span><span class="kwrd">="Dibuja una paralela a una línea existente"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[PARALELA_DINAMICA]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="NUMERO 4"</span> <span class="attr">Description</span><span class="kwrd">="Asigna como código activo el de la línea seleccionada"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[clonar]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="NUMERO 5"</span> <span class="attr">Description</span><span class="kwrd">="Finaliza la línea que está dibujando cerrándola"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[cierra_ent]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="NUMERO 7"</span> <span class="attr">Description</span><span class="kwrd">="Cambia el sentido de la línea seleccionada"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[CAMB_SEN]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="NUMERO 8"</span> <span class="attr">Description</span><span class="kwrd">="Une las dos líneas seleccionadas"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[UNIR]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="P"</span> <span class="attr">Shift</span><span class="kwrd">="true"</span> <span class="attr">Description</span><span class="kwrd">="Matorral"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[cod=58]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="A"</span> <span class="attr">Description</span><span class="kwrd">="Construcción"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[cod=30
modob=2 0]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="G"</span> <span class="attr">Description</span><span class="kwrd">="Muro malla"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[cod=35
modob=2 0]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="H"</span> <span class="attr">Description</span><span class="kwrd">="Cerca de alambre"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[cod=37
modob=2 0]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="I"</span> <span class="attr">Description</span><span class="kwrd">="Presa dique malecón"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[COD=11
modob=2 5]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="K"</span> <span class="attr">Description</span><span class="kwrd">="Antena"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[cod=34
modob=5]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="5"</span> <span class="attr">Description</span><span class="kwrd">="Tentativo a línea en 3D"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[modob=5]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="'"</span> <span class="attr">Description</span><span class="kwrd">="Punto de cota fotogramétrica"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[cod=41
punto
modob=5
cota]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">Keyboard</span><span class="kwrd">&gt;</span></pre>
<p>en esto otro:</p>
<h3>
        Listado de teclas</h3>
<table border="1">
<tr>
<td>
                Tecla
            </td>
<td>
                Mayúsculas
            </td>
<td>
                Control
            </td>
<td>
                Descripción
            </td>
</tr>
<tr>
<td>
                NUMERO 0
            </td>
<td />
<td />
<td>
                Cambia el tamaño del cursor
            </td>
</tr>
<tr>
<td>
                NUMERO 1
            </td>
<td />
<td />
<td>
                Borra entidades por ventana
            </td>
</tr>
<tr>
<td>
                TECLA DE ADICION
            </td>
<td />
<td />
<td>
                Selecciona el código activo
            </td>
</tr>
<tr>
<td>
                NUMERO 9
            </td>
<td />
<td />
<td>
                Dibuja un cruce de caminos
            </td>
</tr>
<tr>
<td>
                NUMERO 6
            </td>
<td />
<td />
<td>
                Dibuja una paralela a una línea existente
            </td>
</tr>
<tr>
<td>
                NUMERO 4
            </td>
<td />
<td />
<td>
                Asigna como código activo el de la línea seleccionada
            </td>
</tr>
<tr>
<td>
                NUMERO 5
            </td>
<td />
<td />
<td>
                Finaliza la línea que está dibujando cerrándola
            </td>
</tr>
<tr>
<td>
                NUMERO 7
            </td>
<td />
<td />
<td>
                Cambia el sentido de la línea seleccionada
            </td>
</tr>
<tr>
<td>
                NUMERO 8
            </td>
<td />
<td />
<td>
                Une las dos líneas seleccionadas
            </td>
</tr>
<tr>
<td>
                P
            </td>
<td>
                X
            </td>
<td />
<td>
                Matorral
            </td>
</tr>
<tr>
<td>
                A
            </td>
<td />
<td />
<td>
                Construcción
            </td>
</tr>
<tr>
<td>
                G
            </td>
<td />
<td />
<td>
                Muro malla
            </td>
</tr>
<tr>
<td>
                H
            </td>
<td />
<td />
<td>
                Cerca de alambre
            </td>
</tr>
<tr>
<td>
                I
            </td>
<td />
<td />
<td>
                Presa dique malecón
            </td>
</tr>
<tr>
<td>
                K
            </td>
<td />
<td />
<td>
                Antena
            </td>
</tr>
<tr>
<td>
                5
            </td>
<td />
<td />
<td>
                Tentativo a línea en 3D
            </td>
</tr>
<tr>
<td>
                &#8216;
            </td>
<td />
<td />
<td>
                Punto de cota fotogramétrica
            </td>
</tr>
</table>
<p>Para realizar nuestra tarea lo único que tenemos que hacer es crear una transformación xml que transforme nuestro documento original en un archivo .html básicamente con una tabla con cuatro columnas, en la primera pondremos la tecla en cuestión, en la segunda una X si estaba pulsada la tecla mayúsculas, en la tercera una X si estaba pulsada la tecla control y por último la descripción.</p>
<p>Estas transformaciones de un documento .xml a otro tipo de documento (que podría ser otro .xml, un archivo .html, o un archivo .txt por ejemplo) se realizan mediente <a href="http://www.w3.org/TR/xslt" title="Transformaciones XSL">Transformaciones XSL</a> que es un estándar precisamente para esto, transformar un archivo .xml en otra cosa.</p>
<p>No te voy a enseñar aquí XSLT, para eso existen multitud de tutoriales en internet. Yo no soy amigo de los tutoriales, prefiero comprarme un buen libro. Personalmente aprendí XSLT (y todo lo relacionado con XML) con el libro <a href="http://www.amazon.es/Essential-XML-Quick-Reference-DevelopMentor/dp/0201740958/ref=sr_1_1?ie=UTF8&#038;qid=1316603346&#038;sr=8-1" title="Essential XML Quick Reference: A Programmer's Reference to XML, XPath, XSLT, XML Schema, SOAP, and More (DevelopMentor)">Essential XML Quick Reference: A Programmer&#8217;s Reference to XML, XPath, XSLT, XML Schema, SOAP, and More (DevelopMentor)</a></p>
<p><strong>Primer paso: Indicar que nuestro archivo de teclas debe ser transformado mediante una transformación XSL</strong></p>
<p>Para ello, lo único que tenemos que hacer es añadir la instrucción de preprocesador <a href="http://www.w3.org/TR/xml-stylesheet/" title="xml-stylesheet">xml-stylesheet</a> en nuestro archivo de teclas haciendo una referencia al archivo .xls en el que vamos a implementar la transformación:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">&lt;?</span><span class="html">xml-stylesheet</span> <span class="attr">type</span><span class="kwrd">="text/xsl"</span> <span class="attr">href</span><span class="kwrd">="keyboard.xsl"</span> ?<span class="kwrd">&gt;</span>
</pre>
<p>Vamos a poner nuestra instrucción al comienzo del documento, después de la instrucción xml, y antes del nodo raiz, de modo que las primeras líneas de nuestro archivo .xml original son las siguientes:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">&lt;?</span><span class="html">xml</span> <span class="attr">version</span><span class="kwrd">="1.0"</span> <span class="attr">encoding</span><span class="kwrd">="utf-8"</span>?<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;?</span><span class="html">xml-stylesheet</span> <span class="attr">type</span><span class="kwrd">="text/xsl"</span> <span class="attr">href</span><span class="kwrd">="keyboard.xsl"</span> ?<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">Keyboard</span> <span class="attr">xmlns</span><span class="kwrd">="http://schemas.digi21.net/Digi3D/keyboard/v1.0"</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="NUMERO 0"</span> <span class="attr">Description</span><span class="kwrd">="Cambia el tamaño del cursor"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[CURSOR]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="NUMERO 1"</span> <span class="attr">Description</span><span class="kwrd">="Borra entidades por ventana"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[BORRA_V]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">Key</span> <span class="attr">Name</span><span class="kwrd">="TECLA DE ADICION"</span> <span class="attr">Description</span><span class="kwrd">="Selecciona el código activo"</span><span class="kwrd">&gt;&lt;!</span>[CDATA[COD]]<span class="kwrd">&gt;&lt;/</span><span class="html">Key</span><span class="kwrd">&gt;</span>
</pre>
<p></p>
<p><strong>Segundo paso: Crear la transformación</strong></p>
<p>Ahora únicamente nos queda crear nuestro archivo de transformación. Creamos el archivo <em>keyboard.xls</em> con la siguiente información:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">&lt;?</span><span class="html">xml</span> <span class="attr">version</span><span class="kwrd">="1.0"</span> <span class="attr">encoding</span><span class="kwrd">="utf-8"</span>?<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">xsl:stylesheet</span> <span class="attr">version</span><span class="kwrd">="1.0"</span>
                <span class="attr">xmlns:xsl</span><span class="kwrd">="http://www.w3.org/1999/XSL/Transform"</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">xsl:stylesheet</span><span class="kwrd">&gt;</span>
</pre>
<p>A continuación añadimos la plantilla de transformación indicando sobre que nodo o conjunto de nodos queremos realizar la transformación, para ello indicamos la cadena <a href="http://www.w3.org/TR/xpath/" title="XPath">XPath</a>:
<pre>/Keyboard</pre>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">&lt;?</span><span class="html">xml</span> <span class="attr">version</span><span class="kwrd">="1.0"</span> <span class="attr">encoding</span><span class="kwrd">="utf-8"</span>?<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">xsl:stylesheet</span> <span class="attr">version</span><span class="kwrd">="1.0"</span>
                <span class="attr">xmlns:xsl</span><span class="kwrd">="http://www.w3.org/1999/XSL/Transform"</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">xsl:template</span> <span class="attr">match</span><span class="kwrd">="/Keyboard"</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;/</span><span class="html">xsl:template</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">xsl:stylesheet</span><span class="kwrd">&gt;</span>
</pre>
<p>y a continuación creamos el cuerpo del archivo html a crear:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">&lt;?</span><span class="html">xml</span> <span class="attr">version</span><span class="kwrd">="1.0"</span> <span class="attr">encoding</span><span class="kwrd">="utf-8"</span>?<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">xsl:stylesheet</span> <span class="attr">version</span><span class="kwrd">="1.0"</span>
                <span class="attr">xmlns:xsl</span><span class="kwrd">="http://www.w3.org/1999/XSL/Transform"</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">xsl:template</span> <span class="attr">match</span><span class="kwrd">="/Keyboard"</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;</span><span class="html">html</span> <span class="attr">xmlns</span><span class="kwrd">="http://www.w3.org/1999/xhtml"</span><span class="kwrd">&gt;</span>
      <span class="kwrd">&lt;</span><span class="html">title</span><span class="kwrd">&gt;</span>Listado de teclas<span class="kwrd">&lt;/</span><span class="html">title</span><span class="kwrd">&gt;</span>
      <span class="kwrd">&lt;</span><span class="html">body</span><span class="kwrd">&gt;</span>
        <span class="kwrd">&lt;</span><span class="html">h3</span><span class="kwrd">&gt;</span>Listado de teclas<span class="kwrd">&lt;/</span><span class="html">h3</span><span class="kwrd">&gt;</span>

        <span class="kwrd">&lt;</span><span class="html">table</span> <span class="attr">border</span><span class="kwrd">="1"</span><span class="kwrd">&gt;</span>
          <span class="kwrd">&lt;</span><span class="html">tr</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Tecla<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Mayúsculas<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Control<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Descripción<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
          <span class="kwrd">&lt;/</span><span class="html">tr</span><span class="kwrd">&gt;</span>

        <span class="kwrd">&lt;/</span><span class="html">table</span><span class="kwrd">&gt;</span>
      <span class="kwrd">&lt;/</span><span class="html">body</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;/</span><span class="html">html</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;/</span><span class="html">xsl:template</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">xsl:stylesheet</span><span class="kwrd">&gt;</span>
</pre>
<p>Tan solo nos queda añadir una entrada por cada nodo hijo <em>Key</em> tenga el nodo <em>Keyboard</em> del archivo de teclas original. </p>
<p>Para ello utilizaremos la instrucción <a href="http://msdn.microsoft.com/es-es/library/ms256166(VS.80).aspx" title="for-each">for-each</a> para añadir tantas filas como nodos hijos tenga el nodo <em>Keyboard</em> de la siguiente manera:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">&lt;?</span><span class="html">xml</span> <span class="attr">version</span><span class="kwrd">="1.0"</span> <span class="attr">encoding</span><span class="kwrd">="utf-8"</span>?<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">xsl:stylesheet</span> <span class="attr">version</span><span class="kwrd">="1.0"</span>
                <span class="attr">xmlns:xsl</span><span class="kwrd">="http://www.w3.org/1999/XSL/Transform"</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">xsl:template</span> <span class="attr">match</span><span class="kwrd">="/Keyboard"</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;</span><span class="html">html</span> <span class="attr">xmlns</span><span class="kwrd">="http://www.w3.org/1999/xhtml"</span><span class="kwrd">&gt;</span>
      <span class="kwrd">&lt;</span><span class="html">title</span><span class="kwrd">&gt;</span>Listado de teclas<span class="kwrd">&lt;/</span><span class="html">title</span><span class="kwrd">&gt;</span>
      <span class="kwrd">&lt;</span><span class="html">body</span><span class="kwrd">&gt;</span>
        <span class="kwrd">&lt;</span><span class="html">h3</span><span class="kwrd">&gt;</span>Listado de teclas<span class="kwrd">&lt;/</span><span class="html">h3</span><span class="kwrd">&gt;</span>

        <span class="kwrd">&lt;</span><span class="html">table</span> <span class="attr">border</span><span class="kwrd">="1"</span><span class="kwrd">&gt;</span>
          <span class="kwrd">&lt;</span><span class="html">tr</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Tecla<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Mayúsculas<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Control<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Descripción<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
          <span class="kwrd">&lt;/</span><span class="html">tr</span><span class="kwrd">&gt;</span>

          <span class="kwrd">&lt;</span><span class="html">xsl:for-each</span> <span class="attr">select</span><span class="kwrd">="Key"</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">tr</span><span class="kwrd">&gt;</span>
              <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>
              <span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>

              <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>
              <span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>

              <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>
              <span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>

              <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>
              <span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;/</span><span class="html">tr</span><span class="kwrd">&gt;</span>
          <span class="kwrd">&lt;/</span><span class="html">xsl:for-each</span><span class="kwrd">&gt;</span>
        <span class="kwrd">&lt;/</span><span class="html">table</span><span class="kwrd">&gt;</span>
      <span class="kwrd">&lt;/</span><span class="html">body</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;/</span><span class="html">html</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;/</span><span class="html">xsl:template</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">xsl:stylesheet</span><span class="kwrd">&gt;</span>
</pre>
<p>y para terminar tendremos que rellenar cada uno de los campos con el valor. </p>
<p>Para añadir el nombre de la tecla en el primer campo, utilizaremos la instrucción <a href="http://msdn.microsoft.com/es-es/library/ms256181(v=VS.80).aspx" title="value-of">value-of</a>, indicando que queremos almacenar el valor del atributo <em>Name</em>. Haremos lo mismo con el último campo, donde añadiremos la información del atributo <em>Description</em>.</p>
<p>Para los campos <em>Mayúsculas</em> y <em>Control</em> podemos utilizar la instrucción <a href="http://msdn.microsoft.com/es-es/library/ms256209(v=VS.80).aspx" title="if">if</a>, indicando que nos introduzca el carácter X únicamente si los atributos <em>Shift</em> o <em>Control</em> tienen asignado el valor verdadero.</p>
<p>Nuestra transformación tiene la siguiente forma:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">&lt;?</span><span class="html">xml</span> <span class="attr">version</span><span class="kwrd">="1.0"</span> <span class="attr">encoding</span><span class="kwrd">="utf-8"</span>?<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">xsl:stylesheet</span> <span class="attr">version</span><span class="kwrd">="1.0"</span>
                <span class="attr">xmlns:xsl</span><span class="kwrd">="http://www.w3.org/1999/XSL/Transform"</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">xsl:template</span> <span class="attr">match</span><span class="kwrd">="/Keyboard"</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;</span><span class="html">html</span> <span class="attr">xmlns</span><span class="kwrd">="http://www.w3.org/1999/xhtml"</span><span class="kwrd">&gt;</span>
      <span class="kwrd">&lt;</span><span class="html">title</span><span class="kwrd">&gt;</span>Listado de teclas<span class="kwrd">&lt;/</span><span class="html">title</span><span class="kwrd">&gt;</span>
      <span class="kwrd">&lt;</span><span class="html">body</span><span class="kwrd">&gt;</span>
        <span class="kwrd">&lt;</span><span class="html">h3</span><span class="kwrd">&gt;</span>Listado de teclas<span class="kwrd">&lt;/</span><span class="html">h3</span><span class="kwrd">&gt;</span>

        <span class="kwrd">&lt;</span><span class="html">table</span> <span class="attr">border</span><span class="kwrd">="1"</span><span class="kwrd">&gt;</span>
          <span class="kwrd">&lt;</span><span class="html">tr</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Tecla<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Mayúsculas<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Control<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Descripción<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
          <span class="kwrd">&lt;/</span><span class="html">tr</span><span class="kwrd">&gt;</span>

          <span class="kwrd">&lt;</span><span class="html">xsl:for-each</span> <span class="attr">select</span><span class="kwrd">="Key"</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">tr</span><span class="kwrd">&gt;</span>
              <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>
                <span class="kwrd">&lt;</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="@Name"</span><span class="kwrd">/&gt;</span>
              <span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>

              <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>
                <span class="kwrd">&lt;</span><span class="html">xsl:if</span> <span class="attr">test</span><span class="kwrd">="@Shift"</span><span class="kwrd">&gt;</span>X<span class="kwrd">&lt;/</span><span class="html">xsl:if</span><span class="kwrd">&gt;</span>
              <span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>

              <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>
                <span class="kwrd">&lt;</span><span class="html">xsl:if</span> <span class="attr">test</span><span class="kwrd">="@Control"</span><span class="kwrd">&gt;</span>X<span class="kwrd">&lt;/</span><span class="html">xsl:if</span><span class="kwrd">&gt;</span>
              <span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>

              <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>
                <span class="kwrd">&lt;</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="@Description"</span><span class="kwrd">/&gt;</span>
              <span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;/</span><span class="html">tr</span><span class="kwrd">&gt;</span>
          <span class="kwrd">&lt;/</span><span class="html">xsl:for-each</span><span class="kwrd">&gt;</span>
        <span class="kwrd">&lt;/</span><span class="html">table</span><span class="kwrd">&gt;</span>
      <span class="kwrd">&lt;/</span><span class="html">body</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;/</span><span class="html">html</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;/</span><span class="html">xsl:template</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">xsl:stylesheet</span><span class="kwrd">&gt;</span>
</pre>
<p>Pero aún no funcionará, ya que todos los nodos del archivo de teclas están almacenados en el espacio de nombres <em>http://schemas.digi21.net/Digi3D/keyboard/v1.0</em>, por lo tanto las consultas XPath a la hora de realizar la transformación no localizarán ni el nodo <em>Keyboard</em> ni el nodo <em>Key</em>.</p>
<p>Para solucionar nuestro problema y finalizar el ejercicio, tan solo nos queda añadir una definición de espacio de nombres a nuestro archivo de transformación xsl que denominaremos <em>teclasMnu</em> y que apuntará a la url: <em>http://schemas.digi21.net/Digi3D/keyboard/v1.0</em> y sustituiremos las consultas XPath <em>/Keyboard</em> por <em>teclasMnu:Keyboard</em> y <em>Key</em> por <em>teclasMnu:Key</em>:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">&lt;?</span><span class="html">xml</span> <span class="attr">version</span><span class="kwrd">="1.0"</span> <span class="attr">encoding</span><span class="kwrd">="utf-8"</span>?<span class="kwrd">&gt;</span>
<span class="kwrd">&lt;</span><span class="html">xsl:stylesheet</span> <span class="attr">version</span><span class="kwrd">="1.0"</span>
                <span class="attr">xmlns:xsl</span><span class="kwrd">="http://www.w3.org/1999/XSL/Transform"</span>
                <span class="attr">xmlns:teclasMnu</span><span class="kwrd">="http://schemas.digi21.net/Digi3D/keyboard/v1.0"</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;</span><span class="html">xsl:template</span> <span class="attr">match</span><span class="kwrd">="/teclasMnu:Keyboard"</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;</span><span class="html">html</span> <span class="attr">xmlns</span><span class="kwrd">="http://www.w3.org/1999/xhtml"</span><span class="kwrd">&gt;</span>
      <span class="kwrd">&lt;</span><span class="html">title</span><span class="kwrd">&gt;</span>Listado de teclas<span class="kwrd">&lt;/</span><span class="html">title</span><span class="kwrd">&gt;</span>
      <span class="kwrd">&lt;</span><span class="html">body</span><span class="kwrd">&gt;</span>
        <span class="kwrd">&lt;</span><span class="html">h3</span><span class="kwrd">&gt;</span>Listado de teclas<span class="kwrd">&lt;/</span><span class="html">h3</span><span class="kwrd">&gt;</span>

        <span class="kwrd">&lt;</span><span class="html">table</span> <span class="attr">border</span><span class="kwrd">="1"</span><span class="kwrd">&gt;</span>
          <span class="kwrd">&lt;</span><span class="html">tr</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Tecla<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Mayúsculas<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Control<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>Descripción<span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
          <span class="kwrd">&lt;/</span><span class="html">tr</span><span class="kwrd">&gt;</span>

          <span class="kwrd">&lt;</span><span class="html">xsl:for-each</span> <span class="attr">select</span><span class="kwrd">="teclasMnu:Key"</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;</span><span class="html">tr</span><span class="kwrd">&gt;</span>
              <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>
                <span class="kwrd">&lt;</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="@Name"</span><span class="kwrd">/&gt;</span>
              <span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>

              <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>
                <span class="kwrd">&lt;</span><span class="html">xsl:if</span> <span class="attr">test</span><span class="kwrd">="@Shift"</span><span class="kwrd">&gt;</span>X<span class="kwrd">&lt;/</span><span class="html">xsl:if</span><span class="kwrd">&gt;</span>
              <span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>

              <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>
                <span class="kwrd">&lt;</span><span class="html">xsl:if</span> <span class="attr">test</span><span class="kwrd">="@Control"</span><span class="kwrd">&gt;</span>X<span class="kwrd">&lt;/</span><span class="html">xsl:if</span><span class="kwrd">&gt;</span>
              <span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>

              <span class="kwrd">&lt;</span><span class="html">td</span><span class="kwrd">&gt;</span>
                <span class="kwrd">&lt;</span><span class="html">xsl:value-of</span> <span class="attr">select</span><span class="kwrd">="@Description"</span><span class="kwrd">/&gt;</span>
              <span class="kwrd">&lt;/</span><span class="html">td</span><span class="kwrd">&gt;</span>
            <span class="kwrd">&lt;/</span><span class="html">tr</span><span class="kwrd">&gt;</span>
          <span class="kwrd">&lt;/</span><span class="html">xsl:for-each</span><span class="kwrd">&gt;</span>
        <span class="kwrd">&lt;/</span><span class="html">table</span><span class="kwrd">&gt;</span>
      <span class="kwrd">&lt;/</span><span class="html">body</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;/</span><span class="html">html</span><span class="kwrd">&gt;</span>
  <span class="kwrd">&lt;/</span><span class="html">xsl:template</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">xsl:stylesheet</span><span class="kwrd">&gt;</span>
</pre>
<p>Y ya hemos terminado. Te propongo que añadas un campo más, para indicar si estaba pulsada la tecla <em>Alt</em> y que además utilices una hoja de estilos en cascada para darle un aspecto más moderno.</p>
<p><a href="http://blogdigi3d.digi21.net/wp-content/uploads/2011/09/teclasmnuAhtml.zip" title="Descarga los archivos de ejemplo de este ejercicio.">Descarga los archivos de ejemplo de este ejercicio.</a></p>
Here is no comments yet by the time  your rss reader get this, Do you want to be the first commentor? Hurry up ]]></content:encoded>
			<wfw:commentRss>http://blogdigi3d.digi21.net/?feed=rss2&#038;p=600</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Detectando líneas sin continuidad</title>
		<link>http://blogdigi3d.digi21.net/?p=471</link>
		<comments>http://blogdigi3d.digi21.net/?p=471#comments</comments>
		<pubDate>Wed, 29 Jun 2011 10:27:48 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[DigiNG]]></category>
		<category><![CDATA[código fuente]]></category>
		<category><![CDATA[continuidad]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[topología]]></category>

		<guid isPermaLink="false">http://blogdigi3d.digi21.net/?p=471</guid>
		<description><![CDATA[Aquí el vídeo relacionado con esta entrada En ocasiones nos interesa realizar análisis de continuidad geométrica. Quizás queramos detectar como error líneas que no tienen continuidad, es decir, que en las coordenadas de alguno de sus extremos no nace otra línea, o quizás justo lo contrario, una línea que continúa con otra con un código [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.screencast.com/t/xw1frMzsm2Sf">Aquí el vídeo relacionado con esta entrada</a></p>
<p>En ocasiones nos interesa realizar análisis de continuidad geométrica.</p>
<p>Quizás queramos detectar como error líneas que no tienen continuidad, es decir, que en las coordenadas de alguno de sus extremos no nace otra línea, o quizás justo lo contrario, una línea que continúa con otra con un código que las hace incompatibles, como curva de nivel fina con curva de nivel maestra, o dos curvas de nivel finas pero con distinta coordenada Z, &#8230;</p>
<p>Si queremos hacer un control de bordes, quizás nos interese detectar como errores líneas que en la práctica no tienen la obligación de continuar con otra línea, pero que finalizan en un marco de hoja y no continúan en otro archivo.</p>
<p>Todos estos análisis los podemos realizar mediante el tipo <em>Digi21.DigiNG.Topology.NodeDetector</em> que implementa una serie de métodos de extensión que se ejecutan sobre una secuencia de líneas y que nos devolverá una secuencia de <em>IGrouping&lt;Point2D, VertexPointer&gt;</em>, es decir, una secuencia de entidades agrupadas por el punto en el que coinciden esas entidades.</p>
<p>El tipo <em>Digi21.DigiNG.Entities.VertexPointer</em> es muy parecido al tipo <em>Digi21.DigiNG.Entities.SegmentPointer</em>, dispone de dos propiedades: una para indicar la línea que llega al nodo y otra para indicar el vértice de esa línea que llega al nodo.</p>
<p>A continuación la definición de este tipo:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">namespace</span> Digi21.DigiNG.Entities
{
    <span class="kwrd">public</span> <span class="kwrd">struct</span> VertexPointer
    {
        <span class="kwrd">public</span> VertexPointer(ReadOnlyLine line, <span class="kwrd">int</span> vertex);

        <span class="kwrd">public</span> ReadOnlyLine Line { get; }
        <span class="kwrd">public</span> <span class="kwrd">int</span> Vertex { get; }
    }
}
</pre>
</p>
<p>Al igual que en el caso del detector de intersecciones, dispondemos de varias sobrecargas del método de extensión que nos permitirán o detectar todos los nodos, o especificar si permitimos analizar una determinada entidad o si nos interesa un nodo en unas determinadas coordenadas o una combinación de estas opciones.</p>
<p>Veamos un ejemplo muy sencillo: vamos a crear una orden que añade tantas tareas en la ventana de tareas como entidades que no tienen continuidad.<br />
Lo que vamos a hacer es detectar todos los nodos y crear una consulta <em>Linq</em> que se quede únicamente con la secuencia de nodos que estén formados únicamente por una entidad.</p>
<p>Si tenemos dos líneas, A y B, ambas formadas por dos puntos, y con las siguientes coordenadas: A: (100, 100) &#8211; (200, 200) y B: (200, 200) &#8211; (300, 100) tendremos tres nodos, uno en las coordenadas (100, 100) al que llega únicamente una entidad, la entidad A, un segundo nodo (200, 200) al que llegan dos entidades, la A y la B y por último el tercer nodo (300, 100) al que llega únicamente la entidad B.</p>
<p>Está claro que los nodos A y B son nodos en los que no hay continuidad pues únicamente llega a ellos una línea.</p>
<p>Aquí el código de la orden:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Command;
<span class="kwrd">using</span> Digi21.DigiNG.Topology;
<span class="kwrd">using</span> Digi21.DigiNG;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;
<span class="kwrd">using</span> Digi21.Digi3D;
<span class="kwrd">using</span> Digi21.Math;

<span class="kwrd">namespace</span> Acme
{
    [Command(Name=<span class="str">"detectar_líneas_sin_continuidad"</span>)]
    <span class="kwrd">public</span> <span class="kwrd">class</span> DetectarLíneasSinContinuidad : Command
    {
        <span class="kwrd">public</span> DetectarLíneasSinContinuidad()
        {
            <span class="kwrd">this</span>.Initialize += <span class="kwrd">new</span> EventHandler(DetectarLíneasSinContinuidad_Initialize);
        }

        <span class="kwrd">void</span> DetectarLíneasSinContinuidad_Initialize(<span class="kwrd">object</span> sender, EventArgs e)
        {
            <span class="kwrd">try</span>
            {
                var todosLosNodos = DigiNG.DrawingFile.OfType&lt;ReadOnlyLine&gt;().DetectNodes();

                var nodosConUnaÚnicaLínea = from nodo <span class="kwrd">in</span> todosLosNodos
                                            <span class="kwrd">where</span> nodo.Count() == 1
                                            select nodo;

                <span class="kwrd">foreach</span> (var nodo <span class="kwrd">in</span> nodosConUnaÚnicaLínea)
                    Digi3D.Tasks.Add(<span class="kwrd">new</span> TaskEntityGotoPoint(
                        (Point3D)nodo.Key,
                        nodo.ElementAt(0).Line,
                        2,
                        <span class="str">"Extremo de línea sin continuidad"</span>,
                        TaskSeverity.Error,
                        DigiNG.DrawingFile.Path,
                        <span class="str">"detectar_líneas_sin_continuidad"</span>));
                DigiNG.RenderScene();
            }
            <span class="kwrd">finally</span>
            {
                Dispose();
            }
        }
    }
}
</pre>
</p>
<p>Muy sencillo, ¿no?. Con esto hemos simulado la opción de <em>Bintram</em> que marca como errores entidades sin continuidad.</p>
<p>Vamos a añadir una tabla de códigos a nuestra orden. Ahora la orden va tener en cuenta únicamente las líneas que tengan alguno de los códigos pasados por parámetros:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Command;
<span class="kwrd">using</span> Digi21.DigiNG.Topology;
<span class="kwrd">using</span> Digi21.DigiNG;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;
<span class="kwrd">using</span> Digi21.Digi3D;
<span class="kwrd">using</span> Digi21.Math;
<span class="kwrd">using</span> UtilidadesDigi;

<span class="kwrd">namespace</span> Acme
{
    [Command(Name=<span class="str">"detectar_líneas_sin_continuidad"</span>)]
    <span class="kwrd">public</span> <span class="kwrd">class</span> DetectarLíneasSinContinuidad : Command
    {
        <span class="kwrd">public</span> DetectarLíneasSinContinuidad()
        {
            <span class="kwrd">this</span>.Initialize += <span class="kwrd">new</span> EventHandler(DetectarLíneasSinContinuidad_Initialize);
        }

        <span class="kwrd">void</span> DetectarLíneasSinContinuidad_Initialize(<span class="kwrd">object</span> sender, EventArgs e)
        {
            <span class="kwrd">try</span>
            {
                <span class="kwrd">if</span> (<span class="kwrd">this</span>.Args.Length == 0)
                {
                    Digi3D.Music(MusicType.Error);
                    Digi3D.ShowBallon(
                        <span class="str">"detectar_líneas_sin_continuidad"</span>,
                        <span class="str">"No has indicado los códigos de las entidades a analizar"</span>,
                        2,
                        BallonIcon.Error);
                    <span class="kwrd">return</span>;
                }

<span class="rem">//                var todosLosNodos = DigiNG.DrawingFile.QueTenganAlgúnCódigo(this.Args).SoloLíneas().DetectNodes();</span>
                var todosLosNodos = DigiNG.DrawingFile.
                    SoloLíneasSinEliminar().DetectNodes(
                    entidad =&gt; entidad.TieneAlgúnCódigo(<span class="kwrd">this</span>.Args));

                var nodosConUnaÚnicaLínea = from nodo <span class="kwrd">in</span> todosLosNodos
                                            <span class="kwrd">where</span> nodo.Count() == 1
                                            select nodo;

                <span class="kwrd">foreach</span> (var nodo <span class="kwrd">in</span> nodosConUnaÚnicaLínea)
                    Digi3D.Tasks.Add(<span class="kwrd">new</span> TaskEntityGotoPoint(
                        (Point3D)nodo.Key,
                        nodo.ElementAt(0).Line,
                        2,
                        <span class="str">"Extremo de línea sin continuidad"</span>,
                        TaskSeverity.Error,
                        DigiNG.DrawingFile.Path,
                        <span class="str">"detectar_líneas_sin_continuidad"</span>));
                DigiNG.RenderScene();
            }
            <span class="kwrd">finally</span>
            {
                Dispose();
            }
        }
    }
}
</pre></p>
Here is no comments yet by the time  your rss reader get this, Do you want to be the first commentor? Hurry up ]]></content:encoded>
			<wfw:commentRss>http://blogdigi3d.digi21.net/?feed=rss2&#038;p=471</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Evitando &#8220;código duro&#8221; en nuestra orden contabilizadora de entidades</title>
		<link>http://blogdigi3d.digi21.net/?p=556</link>
		<comments>http://blogdigi3d.digi21.net/?p=556#comments</comments>
		<pubDate>Wed, 29 Jun 2011 05:20:55 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[DigiNG]]></category>
		<category><![CDATA[código fuente]]></category>
		<category><![CDATA[programación]]></category>

		<guid isPermaLink="false">http://blogdigi3d.digi21.net/?p=556</guid>
		<description><![CDATA[Aquí el vídeo relacionado con este post En el post anterior desarrollamos una orden que contabilizaba todos los tipos de entidad de todos los archivos cargados. Esta orden tenía programado mediante código duro los distintos casos a contabilizar, como líneas, puntos, textos, &#8230; ¿Que pasaría si nuestra orden en un futuro se ejecuta en una [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.screencast.com/t/oBYcjtnjK">Aquí el vídeo relacionado con este post</a></p>
<p>En el <a href="/?p=535">post anterior</a> desarrollamos una orden que contabilizaba todos los tipos de entidad de todos los archivos cargados.</p>
<p>Esta orden tenía programado mediante <a href="http://es.wikipedia.org/wiki/C%C3%B3digo_duro">código duro</a> los distintos casos a contabilizar, como líneas, puntos, textos, &#8230;</p>
<p>¿Que pasaría si nuestra orden en un futuro se ejecuta en una versión más moderna de Digi3D que admite nuevos tipos de entidades, como por ejemplo esferas?</p>
<p>Pues sencillamente que no sería capaz de contabilizar el número de esferas porque no contempla ese caso.</p>
<p>Siempre que desarrollemos este tipo de órdenes, deberíamos intentar evitar el código duro y hacer que la orden esté preparada para un futuro.</p>
<p></em>Linq <em> nos permite agrupar (mediante la cláusula <a href="http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&#038;l=ES-ES&#038;k=k(GROUP_CSHARPKEYWORD);k(GROUP);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22);k(DevLang-CSHARP)&#038;rd=true">group</a>) una secuencia de objetos mediante una clave, y gracias a que mediante <a href="http://msdn.microsoft.com/es-es/library/cxz4wk15(VS.80).aspx">Reflexión</a> podemos obtener en tiempo de ejecución el tipo de un determinado objeto (llamando al método <a href="http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&#038;l=ES-ES&#038;k=k(SYSTEM.OBJECT.GETTYPE);k(GETTYPE);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22);k(DevLang-CSHARP)&#038;rd=true">Object.GetType()</a>, podríamos modificar nuestra orden y convertirla en algo como lo siguiente:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Command;
<span class="kwrd">using</span> Digi21.DigiNG;
<span class="kwrd">using</span> Digi21.Digi3D;
<span class="kwrd">using</span> UtilidadesDigi;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;

<span class="kwrd">namespace</span> Acme
{
    [Command(Name = <span class="str">"suma_todas_las_entidades"</span>)]
    <span class="kwrd">public</span> <span class="kwrd">class</span> SumaTodasLasEntidades
        : Command
    {
        <span class="kwrd">public</span> SumaTodasLasEntidades()
        {
            <span class="kwrd">this</span>.Initialize += <span class="kwrd">new</span> EventHandler(SumaTodasLasEntidades_Initialize);
        }

        <span class="kwrd">void</span> SumaTodasLasEntidades_Initialize(<span class="kwrd">object</span> sender, EventArgs e)
        {
            <span class="kwrd">try</span>
            {
                var agrupación = from entidad <span class="kwrd">in</span> UtilidadesDigiNG.EnumeraTodasLasEntidades()
                                 group entidad by entidad.GetType().Name;

                <span class="kwrd">foreach</span>(var tiposDeEntidad <span class="kwrd">in</span> agrupación)
                    Digi3D.OutputWindow.WriteLine(
                        <span class="str">"{0}: {1}"</span>,
                        tiposDeEntidad.Key,
                        tiposDeEntidad.Count());
            }
        }
    }
}
</pre>
</p>
<p>De esta manera hemos desvinculado completamente nuestra orden de unos tipos de entidad fijos y está preparada para el futuro.</p>
Here is no comments yet by the time  your rss reader get this, Do you want to be the first commentor? Hurry up ]]></content:encoded>
			<wfw:commentRss>http://blogdigi3d.digi21.net/?feed=rss2&#038;p=556</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Secuencias de entidades de un tipo y enumerando entidades de todos los archivos cargados</title>
		<link>http://blogdigi3d.digi21.net/?p=535</link>
		<comments>http://blogdigi3d.digi21.net/?p=535#comments</comments>
		<pubDate>Tue, 28 Jun 2011 17:41:43 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[DigiNG]]></category>
		<category><![CDATA[código fuente]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[Secuencias]]></category>

		<guid isPermaLink="false">http://blogdigi3d.digi21.net/?p=535</guid>
		<description><![CDATA[Aquí el vídeo relacionado con este post En el post anterior creamos un método de extensión denominado EnumeraEntidades que extendía una secuencia de archivos de dibujo de solo lectura y nos devolvía una secuencia de todas las entidades de todos los archivos de referencia cargados dando la impresión que únicamente había un único archivo. Esta [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.screencast.com/t/zvxoZJpX">Aquí el vídeo relacionado con este post</a></p>
<p>En el <a href="/?p=498">post anterior</a> creamos un método de extensión denominado <em>EnumeraEntidades</em> que extendía una secuencia de archivos de dibujo de solo lectura y nos devolvía una secuencia de todas las entidades de todos los archivos de referencia cargados dando la impresión que únicamente había un único archivo.</p>
<p>Esta secuencia devolvía todas las entidades de los archivos de referencia cargados, independientemente de su tipo.</p>
<p>En este post vamos a crear una sobrecarga de este método de extensión para hacerlo genérico y poder indicar mediante su parámetro genérico el tipo de entidad en el que estamos interesados:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;
<span class="kwrd">using</span> Digi21.DigiNG.IO;

<span class="kwrd">namespace</span> UtilidadesDigi
{
    <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> UtilidadesSecuenciaReadOnlyDrawingFile
    {
        <span class="kwrd">public</span> <span class="kwrd">static</span> IEnumerable&lt;T&gt; EnumeraEntidades&lt;T&gt;(<span class="kwrd">this</span> IEnumerable&lt;ReadOnlyDrawingFile&gt; archivos)
            <span class="kwrd">where</span> T:class
        {
            var entidadesADevolver = from archivo <span class="kwrd">in</span> archivos
                                     from entidad <span class="kwrd">in</span> archivo
                                     <span class="kwrd">where</span> entidad <span class="kwrd">is</span> T
                                     select entidad <span class="kwrd">as</span> T;

            <span class="kwrd">return</span> entidadesADevolver;
        }

    }
}
</pre>
</p>
<p>Si te fijas, he cambiado completamente el algoritmo. Ahora ya no utilizamos <em>yield return</em>, y toda la lógica está expresada como una consulta Linq. Podríamos haberlo hecho así en el post anterior, pero quería enseñarte el funcionamiento de <em>yield return</em>.</p>
<p>Ahora tenemos que cambiar el código de la orden, que ya no va a llamar a <em>CuantasEntidadesDeTipo&lt;T&gt;</em>:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;
<span class="kwrd">using</span> Digi21.DigiNG.IO;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Command;
<span class="kwrd">using</span> Digi21.DigiNG;
<span class="kwrd">using</span> Digi21.Digi3D;
<span class="kwrd">using</span> UtilidadesDigi;

<span class="kwrd">namespace</span> Acme
{
    [Command(Name=<span class="str">"suma_entidades_archivos_referencia"</span>)]
    <span class="kwrd">public</span> <span class="kwrd">class</span> SumaEntidadesArchivosReferencia
        : Command
    {
        <span class="kwrd">public</span> SumaEntidadesArchivosReferencia()
        {
            <span class="kwrd">this</span>.Initialize += <span class="kwrd">new</span> EventHandler(SumaEntidadesArchivosReferencia_Initialize);
        }

        <span class="kwrd">void</span> SumaEntidadesArchivosReferencia_Initialize(<span class="kwrd">object</span> sender, EventArgs e)
        {
            <span class="kwrd">try</span>
            {
                <span class="kwrd">if</span> (DigiNG.ReferenceFiles.Length == 0)
                {
                    Digi3D.Music(MusicType.Error);
                    Digi3D.ShowBallon(
                        <span class="str">"suma_entidades_archivos_referencia"</span>,
                        <span class="str">"No hay ningún archivo de referencia cargado"</span>,
                        2,
                        BallonIcon.Error);
                    <span class="kwrd">return</span>;
                }

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Suma de todos los tipos de entidades de los archivos de referencia cargados:"</span>);

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de líneas: {0}"</span>,
                    DigiNG.ReferenceFiles.EnumeraEntidades&lt;ReadOnlyLine&gt;().Count());

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de puntos: {0}"</span>,
                    DigiNG.ReferenceFiles.EnumeraEntidades&lt;ReadOnlyPoint&gt;().Count());

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de textos: {0}"</span>,
                    DigiNG.ReferenceFiles.EnumeraEntidades&lt;ReadOnlyText&gt;().Count());

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de polígonos: {0}"</span>,
                    DigiNG.ReferenceFiles.EnumeraEntidades&lt;ReadOnlyPolygon&gt;().Count());

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de complejos: {0}"</span>,
                    DigiNG.ReferenceFiles.EnumeraEntidades&lt;ReadOnlyComplex&gt;().Count());
            }
            <span class="kwrd">finally</span>
            {
                Dispose();
            }
        }
    }
}
</pre>
</p>
<p>Ahora vamos a hacer un método más que nos va a enumerar todas las entidades de todos los archivos cargados, tanto del archivo de dibujo como de los archivos de referencia.</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;
<span class="kwrd">using</span> Digi21.DigiNG;

<span class="kwrd">namespace</span> UtilidadesDigi
{
    <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> UtilidadesDigiNG
    {
        <span class="kwrd">public</span> <span class="kwrd">static</span> IEnumerable&lt;Entity&gt; EnumeraTodasLasEntidades()
        {
            <span class="kwrd">foreach</span> (var entidad <span class="kwrd">in</span> DigiNG.DrawingFile)
                <span class="kwrd">yield</span> <span class="kwrd">return</span> entidad;

            <span class="kwrd">foreach</span> (var archivoReferencia <span class="kwrd">in</span> DigiNG.ReferenceFiles)
                <span class="kwrd">foreach</span> (var entidad <span class="kwrd">in</span> archivoReferencia)
                    <span class="kwrd">yield</span> <span class="kwrd">return</span> entidad;
        }

        <span class="kwrd">public</span> <span class="kwrd">static</span> IEnumerable&lt;T&gt; EnumeraTodasLasEntidades&lt;T&gt;()
            <span class="kwrd">where</span> T:<span class="kwrd">class</span>
        {
            <span class="kwrd">foreach</span> (var entidad <span class="kwrd">in</span> DigiNG.DrawingFile)
            {
                <span class="kwrd">if</span>( entidad <span class="kwrd">is</span> T)
                    <span class="kwrd">yield</span> <span class="kwrd">return</span> entidad <span class="kwrd">as</span> T;
            }

            <span class="kwrd">foreach</span> (var archivoReferencia <span class="kwrd">in</span> DigiNG.ReferenceFiles)
            {
                <span class="kwrd">foreach</span> (var entidad <span class="kwrd">in</span> archivoReferencia)
                {
                    <span class="kwrd">if</span>( entidad <span class="kwrd">is</span> T )
                        <span class="kwrd">yield</span> <span class="kwrd">return</span> entidad <span class="kwrd">as</span> T;
                }
            }
        }
    }
}
</pre>
</p>
<p>y para probar su funcionamiento, vamos a crear una orden nueva que va a contabilizar las entidades de cada tipo de todos los archivos cargados:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Command;
<span class="kwrd">using</span> Digi21.DigiNG;
<span class="kwrd">using</span> Digi21.Digi3D;
<span class="kwrd">using</span> UtilidadesDigi;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;

<span class="kwrd">namespace</span> Acme
{
    [Command(Name = <span class="str">"suma_todas_las_entidades"</span>)]
    <span class="kwrd">public</span> <span class="kwrd">class</span> SumaTodasLasEntidades
        : Command
    {
        <span class="kwrd">public</span> SumaTodasLasEntidades()
        {
            <span class="kwrd">this</span>.Initialize += <span class="kwrd">new</span> EventHandler(SumaTodasLasEntidades_Initialize);
        }

        <span class="kwrd">void</span> SumaTodasLasEntidades_Initialize(<span class="kwrd">object</span> sender, EventArgs e)
        {
            <span class="kwrd">try</span>
            {
                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Suma de todos los tipos de entidades de todos los archivos cargados:"</span>);

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de líneas: {0}"</span>,
                    UtilidadesDigiNG.
                    EnumeraTodasLasEntidades&lt;ReadOnlyLine&gt;().Count());

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de puntos: {0}"</span>,
                    UtilidadesDigiNG.
                    EnumeraTodasLasEntidades&lt;ReadOnlyPoint&gt;().Count());

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de textos: {0}"</span>,
                    UtilidadesDigiNG.
                    EnumeraTodasLasEntidades&lt;ReadOnlyText&gt;().Count());

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de polígonos: {0}"</span>,
                    UtilidadesDigiNG.
                    EnumeraTodasLasEntidades&lt;ReadOnlyPolygon&gt;().Count());

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de complejos: {0}"</span>,
                    UtilidadesDigiNG.
                    EnumeraTodasLasEntidades&lt;ReadOnlyComplex&gt;().Count());
            }
            <span class="kwrd">finally</span>
            {
                Dispose();
            }
        }
    }
}
</pre></p>
Here is no comments yet by the time  your rss reader get this, Do you want to be the first commentor? Hurry up ]]></content:encoded>
			<wfw:commentRss>http://blogdigi3d.digi21.net/?feed=rss2&#038;p=535</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Secuencias infinitas con Yield return y creación de una única secuencia de entidades de todos los archivos de referencia</title>
		<link>http://blogdigi3d.digi21.net/?p=498</link>
		<comments>http://blogdigi3d.digi21.net/?p=498#comments</comments>
		<pubDate>Tue, 28 Jun 2011 14:03:26 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[DigiNG]]></category>
		<category><![CDATA[archivos de referencia]]></category>
		<category><![CDATA[código fuente]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[Secuencias]]></category>

		<guid isPermaLink="false">http://blogdigi3d.digi21.net/?p=498</guid>
		<description><![CDATA[Aquí el vídeo relacionado con este post En ocasiones nos interesará crear un enumerador que enumere las entidades de todos los archivos de referencia como un todo, es decir, como si todos los archivos de referencia fueran un único archivo, por ejemplo cuando realicemos un control de bordes: nos interesará analizar la continuidad de las [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.screencast.com/t/2iyP9CyNyBJ8">Aquí el vídeo relacionado con este post</a></p>
<p>En ocasiones nos interesará crear un enumerador que enumere las entidades de todos los archivos de referencia como un todo, es decir, como si todos los archivos de referencia fueran un único archivo, por ejemplo cuando realicemos un control de bordes: nos interesará analizar la continuidad de las entidades del archivo de dibujo activo con respecto a todos los archivos de dibujo de referencia cargados.</p>
<p>Para ello, vamos a aprovecharnos de una de las maravillas de C# que es la palabra clave <a href="http://msdn.microsoft.com/es-es/library/9k7k7cf0.aspx">yield</a>. Esta palabra clave básicamente nos permite devolver un valor al llamador, pero almacenando el estado en el que hemos devuelvo el control para continuar por el mismo sitio y con los mismos valores si se vuelve a llamar al método que utiliza el yield return.</p>
<p>Por ejemplo, (te voy a explicar el ejemplo más común, pero es que viene de perlas) si queremos hacer un método que devuelva una secuencia de 10 números aleatorios, podríamos implementarlo de la siguiente manera:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
       <span class="kwrd">public</span> IEnumerable&lt;<span class="kwrd">int</span>&gt; SecuenciaNúmerosAleatorios()
        {
            Random r = <span class="kwrd">new</span> Random();

            <span class="kwrd">int</span>[] array = <span class="kwrd">new</span> <span class="kwrd">int</span>[10];
            <span class="kwrd">for</span> (<span class="kwrd">int</span> i = 0; i &lt; 10; i++)
                array[i] = r.Next();

            <span class="kwrd">return</span> array;
        }</pre>
</p>
<p>Como un array de <em>int</em> es siempre un <em>IEnumerable&lt;int&gt;</em> pues tan solo tenemos que crear un array de 10 posiciones, rellenarlo y devolverlo, y estaremos devolviendo una secuencia de 10 números aleatorios. Perfecto.</p>
<p>Ahora, ¿que pasa si te digo que quiero que esa secuencia sea infinita?</p>
<p>No podemos hacer un array infinito porque nuestro ordenador no tiene una cantidad infinita de memoria.</p>
<p>Aquí es donde viene a ayudarnos la palabra clave <em>yield</em>. Mira cómo podríamos hacer ese método como un método que devuelve un número infinito de valores:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
        <span class="kwrd">public</span> IEnumerable&lt;<span class="kwrd">int</span>&gt; SecuenciaNúmerosAleatorios()
        {
            Random r = <span class="kwrd">new</span> Random();

            <span class="kwrd">while</span>(<span class="kwrd">true</span>)
                <span class="kwrd">yield</span> <span class="kwrd">return</span> r.Next();
        }
</pre>
</p>
<p>¿no te parece precioso?</p>
<p><em>Yield</em> lo que hace es implementar una secuencia perezosa, únicamente se generará un nuevo número aleatorio en el momento en el que ordenemos al iterador que se mueva a la siguiente posición, de modo que no hay consumo de memoria ni tiempo de espera para que se rellene el ¿array? ¿que array? de valores.</p>
<p>Bien, pues ahora que conoces la palabra clave <em>yield</em>, vamos a crear un método de extensión que se ejecute sobre una secuenciua de <em>ReadOnlyDrawingFile</em> y que nos devuelva una secuencia única de entidades que devuelva una a una todas las entidades de todos los archivos de referencia cargados.</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;
<span class="kwrd">using</span> Digi21.DigiNG.IO;

<span class="kwrd">namespace</span> UtilidadesDigi
{
    <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> UtilidadesSecuenciaReadOnlyDrawingFile
    {
        <span class="kwrd">public</span> <span class="kwrd">static</span> IEnumerable&lt;Entity&gt; EnumeraEntidades(<span class="kwrd">this</span> IEnumerable&lt;ReadOnlyDrawingFile&gt; archivos)
        {
            <span class="kwrd">foreach</span> (var archivo <span class="kwrd">in</span> archivos)
                <span class="kwrd">foreach</span> (var entidad <span class="kwrd">in</span> archivo)
                    <span class="kwrd">yield</span> <span class="kwrd">return</span> entidad;
        }

    }
}
</pre>
</p>
<p>No se a tí, pero a mí me encanta C#, por cosas como esta.</p>
<p>A continuación vamos a hacer una orden que nos muestre en la ventana de resultados la suma de entidades de cada tipo (líneas, puntos, &#8230;) de todos los archivos de referencia cargados utilizando el método de extensión que acabamos de desarrollar:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;
<span class="kwrd">using</span> Digi21.DigiNG.IO;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Command;
<span class="kwrd">using</span> Digi21.DigiNG;
<span class="kwrd">using</span> Digi21.Digi3D;
<span class="kwrd">using</span> UtilidadesDigi;

<span class="kwrd">namespace</span> Acme
{
    [Command(Name=<span class="str">"suma_entidades_archivos_referencia"</span>)]
    <span class="kwrd">public</span> <span class="kwrd">class</span> SumaEntidadesArchivosReferencia
        : Command
    {
        <span class="kwrd">public</span> SumaEntidadesArchivosReferencia()
        {
            <span class="kwrd">this</span>.Initialize += <span class="kwrd">new</span> EventHandler(SumaEntidadesArchivosReferencia_Initialize);
        }

        <span class="kwrd">void</span> SumaEntidadesArchivosReferencia_Initialize(<span class="kwrd">object</span> sender, EventArgs e)
        {
            <span class="kwrd">try</span>
            {
                <span class="kwrd">if</span> (DigiNG.ReferenceFiles.Length == 0)
                {
                    Digi3D.Music(MusicType.Error);
                    Digi3D.ShowBallon(
                        <span class="str">"suma_entidades_archivos_referencia"</span>,
                        <span class="str">"No hay ningún archivo de referencia cargado"</span>,
                        2,
                        BallonIcon.Error);
                    <span class="kwrd">return</span>;
                }

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Suma de todos los tipos de entidades de los archivos de referencia cargados:"</span>);

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de líneas: {0}"</span>,
                    DigiNG.ReferenceFiles.EnumeraEntidades().
                    CuantasEntidadesDeTipo&lt;ReadOnlyLine&gt;());

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de puntos: {0}"</span>,
                    DigiNG.ReferenceFiles.EnumeraEntidades().
                    CuantasEntidadesDeTipo&lt;ReadOnlyPoint&gt;());

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de textos: {0}"</span>,
                    DigiNG.ReferenceFiles.EnumeraEntidades().
                    CuantasEntidadesDeTipo&lt;ReadOnlyText&gt;());

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de polígonos: {0}"</span>,
                    DigiNG.ReferenceFiles.EnumeraEntidades().
                    CuantasEntidadesDeTipo&lt;ReadOnlyPolygon&gt;());

                Digi3D.OutputWindow.WriteLine(
                    <span class="str">"Número de complejos: {0}"</span>,
                    DigiNG.ReferenceFiles.EnumeraEntidades().
                    CuantasEntidadesDeTipo&lt;ReadOnlyComplex&gt;());
            }
            <span class="kwrd">finally</span>
            {
                Dispose();
            }
        }

    }
}
</pre></p>
Here is no comments yet by the time  your rss reader get this, Do you want to be the first commentor? Hurry up ]]></content:encoded>
			<wfw:commentRss>http://blogdigi3d.digi21.net/?feed=rss2&#038;p=498</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Archivos de referencia</title>
		<link>http://blogdigi3d.digi21.net/?p=472</link>
		<comments>http://blogdigi3d.digi21.net/?p=472#comments</comments>
		<pubDate>Mon, 27 Jun 2011 16:12:43 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[DigiNG]]></category>
		<category><![CDATA[archivos de referencia]]></category>
		<category><![CDATA[código fuente]]></category>
		<category><![CDATA[programación]]></category>

		<guid isPermaLink="false">http://blogdigi3d.digi21.net/?p=472</guid>
		<description><![CDATA[Aquí el vídeo relacionado con este post Digi3D 2011 expone las entidades de los archivos de referencia (aquellos archivos que no son el archivo de dibujo activo) a través de la propiedad DigiNG.ReferenceFiles que devuelve un array de tipo ReadOnlyDrawingFile. El array devuelto tendrá tantos valores como archivos de referencia cargados, y tal y como [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.screencast.com/t/L9Fwi9Pl7i">Aquí el vídeo relacionado con este post</a></p>
<p>Digi3D 2011 expone las entidades de los archivos de referencia (aquellos archivos que no son el archivo de dibujo activo) a través de la propiedad <em>DigiNG.ReferenceFiles</em> que devuelve un array de tipo <em>ReadOnlyDrawingFile</em>.</p>
<p>El array devuelto tendrá tantos valores como archivos de referencia cargados, y tal y como su nombre indica, no podemos realizar ninguna modificación sobre estos archivos, pues nos devuelve enumeradores de solo lectura.</p>
<p>En el siguiente ejemplo, vamos a desarrollar una orden que nos muestra en la ventana de resultados el número de entidades de cada tipo que tiene cada uno de los archivos de referencia cargados.</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Command;
<span class="kwrd">using</span> Digi21.DigiNG.IO;
<span class="kwrd">using</span> Digi21.DigiNG;
<span class="kwrd">using</span> Digi21.Digi3D;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;

<span class="kwrd">namespace</span> Pruebas
{
    <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> UtilidadesSecuenciaEntity
    {
        <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">int</span> CuantasEntidadesDeTipo&lt;T&gt;(<span class="kwrd">this</span> IEnumerable&lt;Entity&gt; entidades)
        {
            var secuenciaDeT = from entidad <span class="kwrd">in</span> entidades
                               <span class="kwrd">where</span> entidad <span class="kwrd">is</span> T
                               select entidad;

            <span class="kwrd">return</span> secuenciaDeT.Count();
        }
    }

    [Command(Name=<span class="str">"cuenta_entidades_archivos_referencia"</span>)]
    <span class="kwrd">public</span> <span class="kwrd">class</span> CuentaEntidadesDeArchivosReferencia : Command
    {
        <span class="kwrd">public</span> CuentaEntidadesDeArchivosReferencia()
        {
            <span class="kwrd">this</span>.Initialize +=
                <span class="kwrd">new</span> EventHandler(CuentaEntidadesDeArchivosReferencia_Initialize);
        }

        <span class="kwrd">void</span> CuentaEntidadesDeArchivosReferencia_Initialize(<span class="kwrd">object</span> sender, EventArgs e)
        {
            <span class="kwrd">try</span>
            {
                var archivosReferencia = DigiNG.ReferenceFiles;

                <span class="kwrd">if</span> (archivosReferencia.Length == 0)
                {
                    Digi3D.Music(MusicType.Error);
                    Digi3D.ShowBallon(
                        <span class="str">"cuenta_entidades_archivos_referencia"</span>,
                        <span class="str">"No hay ningún archivo de referencia cargado"</span>,
                        2,
                        BallonIcon.Error);
                    <span class="kwrd">return</span>;
                }

                <span class="kwrd">foreach</span> (var archivo <span class="kwrd">in</span> DigiNG.ReferenceFiles)
                {
                    Digi3D.OutputWindow.WriteLine(archivo.Path);

                    Digi3D.OutputWindow.WriteLine(
                        <span class="str">"{0} líneas"</span>,
                        archivo.CuantasEntidadesDeTipo&lt;ReadOnlyLine&gt;());

                    Digi3D.OutputWindow.WriteLine(
                        <span class="str">"{0} puntos"</span>,
                        archivo.CuantasEntidadesDeTipo&lt;ReadOnlyPoint&gt;());

                    Digi3D.OutputWindow.WriteLine(
                        <span class="str">"{0} textos"</span>,
                        archivo.CuantasEntidadesDeTipo&lt;ReadOnlyText&gt;());

                    Digi3D.OutputWindow.WriteLine(
                        <span class="str">"{0} polígonos"</span>,
                        archivo.CuantasEntidadesDeTipo&lt;ReadOnlyPolygon&gt;());

                    Digi3D.OutputWindow.WriteLine(
                        <span class="str">"{0} complejos"</span>,
                        archivo.CuantasEntidadesDeTipo&lt;ReadOnlyComplex&gt;());
                }
            }
            <span class="kwrd">finally</span>
            {
                Dispose();
            }
        }
    }
}
</pre></p>
Here is no comments yet by the time  your rss reader get this, Do you want to be the first commentor? Hurry up ]]></content:encoded>
			<wfw:commentRss>http://blogdigi3d.digi21.net/?feed=rss2&#038;p=472</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Detectando las intersecciones de una determinada línea</title>
		<link>http://blogdigi3d.digi21.net/?p=447</link>
		<comments>http://blogdigi3d.digi21.net/?p=447#comments</comments>
		<pubDate>Mon, 27 Jun 2011 13:27:24 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[DigiNG]]></category>
		<category><![CDATA[código fuente]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[topología]]></category>

		<guid isPermaLink="false">http://blogdigi3d.digi21.net/?p=447</guid>
		<description><![CDATA[En el post anterior nos centramos en los métodos de extensión implementados en el tipo Digi21.DigiNG.Topology.IntersectionDetector que extendían secuencias de líneas. En este post vamos a centrarnos en las sobrecargas que actúan únicamente sobre una determinada línea. Aquí tienes el vídeo relacionado con este bloque de código Si queremos averiguar únicamente las intersecciones que tiene [...]]]></description>
			<content:encoded><![CDATA[<p>En el <a href="/?p=379">post anterior</a> nos centramos en los métodos de extensión implementados en el tipo <em>Digi21.DigiNG.Topology.IntersectionDetector</em> que extendían secuencias de líneas.</p>
<p>En este post vamos a centrarnos en las sobrecargas que actúan únicamente sobre una determinada línea.</p>
<p><a href="http://www.screencast.com/t/M4lGIAzqSB">Aquí tienes el vídeo relacionado con este bloque de código</a></p>
<p>Si queremos averiguar únicamente las intersecciones que tiene una línea con el resto de líneas del modelo, podemos utilizar cualquiera de los métodos del post anterior y luego centrarnos únicamente en las intersecciones entre las que esté involucrada la línea que nos interesa.</p>
<p>Veámoslo con un ejemplo: Vamos a hacer una orden que solicita al usuario que se seleccione una línea y luego añadirá tantas tareas como intersecciones tenga esa línea con el resto de líneas del archivo de dibujo.</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode"><span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Command;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Shell;
<span class="kwrd">using</span> Digi21.Digi3D;
<span class="kwrd">using</span> Digi21.DigiNG;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;
<span class="kwrd">using</span> UtilidadesDigi;
<span class="kwrd">using</span> Digi21.DigiNG.Topology;
<span class="kwrd">using</span> Digi21.Math;

<span class="kwrd">namespace</span> Pruebas
{
    [CommandInMenu(
        <span class="str">"Mostrar las intersecciones de la línea seleccionada"</span>,
        MenuItemGroup.GeometricAnalysisGroup1)]
    [Command(Name=<span class="str">"intersecciones_de_línea"</span>)]
    <span class="kwrd">public</span> <span class="kwrd">class</span> InterseccionesDeLínea : Command
    {
        <span class="kwrd">public</span> InterseccionesDeLínea()
        {
            <span class="kwrd">this</span>.Initialize += <span class="kwrd">new</span> EventHandler(InterseccionesDeLínea_SetFocus);
            <span class="kwrd">this</span>.SetFocus += <span class="kwrd">new</span> EventHandler(InterseccionesDeLínea_SetFocus);
            <span class="kwrd">this</span>.DataUp += <span class="kwrd">new</span> EventHandler&lt;Digi21.Math.Point3DEventArgs&gt;(InterseccionesDeLínea_DataUp);
            <span class="kwrd">this</span>.EntitySelected += <span class="kwrd">new</span> EventHandler&lt;EntitySelectedEventArgs&gt;(InterseccionesDeLínea_EntitySelected);
        }

        <span class="kwrd">void</span> InterseccionesDeLínea_SetFocus(<span class="kwrd">object</span> sender, EventArgs e)
        {
            Digi3D.StatusBar.Text = <span class="str">"Selecciona la línea para mostrar sus intersecciones"</span>;
        }

        <span class="kwrd">void</span> InterseccionesDeLínea_DataUp(<span class="kwrd">object</span> sender, Digi21.Math.Point3DEventArgs e)
        {
            DigiNG.SelectEntity(
                e.Coordinates,
                entidad =&gt; entidad <span class="kwrd">is</span> ReadOnlyLine);
        }

        <span class="kwrd">void</span> InterseccionesDeLínea_EntitySelected(<span class="kwrd">object</span> sender, EntitySelectedEventArgs e)
        {
            ReadOnlyLine líneaSeleccionada = (ReadOnlyLine)e.Entity;

            var todasLasIntersecciones = DigiNG.DrawingFile.SoloLíneas().DetectIntersections();
            <span class="kwrd">foreach</span> (var intersección <span class="kwrd">in</span> todasLasIntersecciones)
            {
                <span class="kwrd">foreach</span> (var entidadImplicadaEnLaIntersección <span class="kwrd">in</span> intersección)
                {
                    <span class="kwrd">if</span> (líneaSeleccionada == entidadImplicadaEnLaIntersección.Line)
                    {
                        Digi3D.Tasks.Add(<span class="kwrd">new</span> TaskGotoPoint(
                            (Point3D)intersección.Key,
                            <span class="str">"Intersección"</span>,
                            TaskSeverity.Error,
                            DigiNG.DrawingFile.Path,
                            <span class="str">"intersecciones_de_línea"</span>));
                        DigiNG.RenderScene();
                        <span class="kwrd">break</span>;
                    }
                }
            }

            Dispose();
        }
    }
}</pre>
<p>El problema que tiene este sistema es que estamos analizando las intersecciones existentes en todo el archivo de dibujo para luego centrarnos únicamente en un subconjunto en teoría muy pequeño de todas estas intersecciones de modo que estamos desaprovechando recursos (tiempo y memoria).</p>
<p>Afortunadamente, el tipo <em>Digi21.DigiNG.Topology.IntersectionDetector</em> expone métodos de extensión que afectan a una única línea. Estos métodos de extensión son idénticos a aquellos que se aplican a una secuencia de líneas, pero centrándose únicamente en las intersecciones de una determinada línea, de modo que es mucho más rápido, consume menos memoria y además ya no tenemos que buscar los nodos a los que llega nuestra línea, pues todos ellos cumplirán esta función.</p>
<p>La sobrecarga más sencilla es aquella que recibe como único parámetro el conjunto de líneas con las que queremos comprobar sus intersecciones.</p>
<p>Veamos entonces como queda nuestra orden optimizada con el método de extensión aplicado a líneas:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode"><span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Command;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Shell;
<span class="kwrd">using</span> Digi21.Digi3D;
<span class="kwrd">using</span> Digi21.DigiNG;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;
<span class="kwrd">using</span> UtilidadesDigi;
<span class="kwrd">using</span> Digi21.DigiNG.Topology;
<span class="kwrd">using</span> Digi21.Math;

<span class="kwrd">namespace</span> Pruebas
{
    [CommandInMenu(
        <span class="str">"Mostrar las intersecciones de la línea seleccionada"</span>,
        MenuItemGroup.GeometricAnalysisGroup1)]
    [Command(Name=<span class="str">"intersecciones_de_línea"</span>)]
    <span class="kwrd">public</span> <span class="kwrd">class</span> InterseccionesDeLínea : Command
    {
        <span class="kwrd">public</span> InterseccionesDeLínea()
        {
            <span class="kwrd">this</span>.Initialize += <span class="kwrd">new</span> EventHandler(InterseccionesDeLínea_SetFocus);
            <span class="kwrd">this</span>.SetFocus += <span class="kwrd">new</span> EventHandler(InterseccionesDeLínea_SetFocus);
            <span class="kwrd">this</span>.DataUp += <span class="kwrd">new</span> EventHandler&lt;Digi21.Math.Point3DEventArgs&gt;(InterseccionesDeLínea_DataUp);
            <span class="kwrd">this</span>.EntitySelected += <span class="kwrd">new</span> EventHandler&lt;EntitySelectedEventArgs&gt;(InterseccionesDeLínea_EntitySelected);
        }

        <span class="kwrd">void</span> InterseccionesDeLínea_SetFocus(<span class="kwrd">object</span> sender, EventArgs e)
        {
            Digi3D.StatusBar.Text = <span class="str">"Selecciona la línea para mostrar sus intersecciones"</span>;
        }

        <span class="kwrd">void</span> InterseccionesDeLínea_DataUp(<span class="kwrd">object</span> sender, Digi21.Math.Point3DEventArgs e)
        {
            DigiNG.SelectEntity(
                e.Coordinates,
                entidad =&gt; entidad <span class="kwrd">is</span> ReadOnlyLine);
        }

        <span class="kwrd">void</span> InterseccionesDeLínea_EntitySelected(<span class="kwrd">object</span> sender, EntitySelectedEventArgs e)
        {
            ReadOnlyLine líneaSeleccionada = (ReadOnlyLine)e.Entity;

            var todasLasIntersecciones =
                líneaSeleccionada.DetectIntersections(DigiNG.DrawingFile.SoloLíneas());

            <span class="kwrd">foreach</span> (var intersección <span class="kwrd">in</span> todasLasIntersecciones)
            {
                <span class="kwrd">foreach</span> (var entidadImplicadaEnLaIntersección <span class="kwrd">in</span> intersección)
                {
                    <span class="kwrd">if</span> (líneaSeleccionada == entidadImplicadaEnLaIntersección.Line)
                    {
                        Digi3D.Tasks.Add(<span class="kwrd">new</span> TaskGotoPoint(
                            (Point3D)intersección.Key,
                            <span class="str">"Intersección"</span>,
                            TaskSeverity.Error,
                            DigiNG.DrawingFile.Path,
                            <span class="str">"intersecciones_de_línea"</span>));
                        DigiNG.RenderScene();
                        <span class="kwrd">break</span>;
                    }
                }
            }

            Dispose();
        }
    }
}</pre>
<p>Y como este método es tan rápido, podemos permitirnos el lujo de ejecutarlo en tiempo real.</p>
<p>Vamos a desarrollar una orden que se ejecutará cada vez que el usuario digitaliza una línea. Si esta línea es una curva de nivel, se comprobará mediante este método si esta ha interseccionado con otra curva de nivel, en cuyo caso se mostrará al usuario una tarea, un globo y un sonido de error, con &#8220;sorpresa final&#8221;, de modo que en el mismo instante en el que el usuario digitaliza la línea, el programa ya le informa de que ha cometido un error.</p>
<p>Si te fijas en el código, esta orden no hace una llamada al método <em>Dispose</em>, por lo tanto, nunca se auto destruye. Al ejecutarla se queda residente, a la escucha, esperando que se digitalice una línea. En nomenclatura UNIX esta orden sería un <em>demonio</em>, en nomenclatura Windows sería una especia de <em>proceso</em>.<br />
La única manera de destruir la orden es al finalizar Digi3D, el propio CLR de Windows se encargará de destruirla.</p>
<p>El truco para convertir esta orden en un proceso consiste en la llamada al método <em>DigiNG.Commands.Pop()</em>, que elimina la orden de la pila de órdenes de DigiNG, de modo que aunque el usuario pulse la tecla Escape, no puede destruir la orden, pues la tecla Escape destruye las órdenes que están en la pila de órdenes.</p>
<p>Y ¿cuándo entra en acción la orden?</p>
<p>Cada vez que DigiNG almacena una entidad en el archivo de dibujo, lanza el evento <em>DigiNG.EntityAddes</em>, evento al cual se puede conectar cualquier orden.</p>
<p>Veamos el código de la orden:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Command;
<span class="kwrd">using</span> Digi21.DigiNG;
<span class="kwrd">using</span> Digi21.Digi3D;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;
<span class="kwrd">using</span> UtilidadesDigi;
<span class="kwrd">using</span> Digi21.DigiNG.Topology;
<span class="kwrd">using</span> Digi21.Math;

<span class="kwrd">namespace</span> Acme
{
    [Command(Name=<span class="str">"servicio_curvas_nivel"</span>)]
    <span class="kwrd">public</span> <span class="kwrd">class</span> ServicioCurvasNivel : Command
    {
        <span class="kwrd">public</span> ServicioCurvasNivel()
        {
            <span class="kwrd">this</span>.Initialize += <span class="kwrd">new</span> EventHandler(ServicioCurvasNivel_Initialize);
            DigiNG.EntityAdded += <span class="kwrd">new</span> EventHandler&lt;EntityAddedEventArgs&gt;(DigiNG_EntityAdded);
        }

        <span class="kwrd">void</span> DigiNG_EntityAdded(<span class="kwrd">object</span> sender, EntityAddedEventArgs e)
        {
            <span class="kwrd">if</span> (e.Entity <span class="kwrd">is</span> ReadOnlyLine &amp;&amp;
                e.Entity.TieneAlgúnCódigo(<span class="kwrd">this</span>.Args))
                ControlCalidadCurvasNivel(e.Entity <span class="kwrd">as</span> ReadOnlyLine);
        }

        <span class="kwrd">private</span> <span class="kwrd">void</span> ControlCalidadCurvasNivel(ReadOnlyLine línea)
        {
            var curvasDeNivelExistentes = from entidad <span class="kwrd">in</span> DigiNG.DrawingFile
                                          <span class="kwrd">where</span> entidad != línea
                                          <span class="kwrd">where</span> entidad.TieneAlgúnCódigo(<span class="kwrd">this</span>.Args)
                                          select entidad <span class="kwrd">as</span> ReadOnlyLine;

            var intersecciones = línea.DetectIntersections(curvasDeNivelExistentes);

            List&lt;ITask&gt; tareasAAñadir = <span class="kwrd">new</span> List&lt;ITask&gt;();
            <span class="kwrd">foreach</span> (var intersección <span class="kwrd">in</span> intersecciones)
            {
                tareasAAñadir.Add(<span class="kwrd">new</span> TaskEntityGotoPoint(
                    (Point3D)intersección.Key,
                    línea,
                    2,
                    <span class="str">"Curva de nivel que cruza con otras curvas de nivel"</span>,
                    TaskSeverity.Error,
                    DigiNG.DrawingFile.Path,
                    <span class="str">"servicio_curvas_nivel"</span>));
            }

            <span class="kwrd">foreach</span> (var tarea <span class="kwrd">in</span> tareasAAñadir)
                Digi3D.Tasks.Add(tarea);

            <span class="kwrd">if</span> (tareasAAñadir.Count != 0)
            {
                Digi3D.Music(MusicType.Error);
                DigiNG.RenderScene();
            }
        }

        <span class="kwrd">void</span> ServicioCurvasNivel_Initialize(<span class="kwrd">object</span> sender, EventArgs e)
        {
            <span class="kwrd">if</span> (<span class="kwrd">this</span>.Args.Length == 0)
            {
                Digi3D.Music(MusicType.Error);
                Digi3D.ShowBallon(
                    <span class="str">"servicio_curvas_nivel"</span>,
                    <span class="str">"No se han indicado los códigos de las curvas de nivel"</span>,
                    2,
                    BallonIcon.Error);
                Dispose();
                <span class="kwrd">return</span>;
            }

            DigiNG.Commands.Pop();
        }
    }
}
</pre>
</p>
<p>Y ahora la &#8220;sorpresa final&#8221;, ¿Por qué no hacer que en vez de sonar el sonido de error de Digi, una voz sintética hable indicándole al usuario en perfecto castellano que la curva de nivel que acaba de digitalizar se cruza X veces con otra curva de nivel?</p>
<p>Si hacemos que nuestro proyecto referencie el ensamblado <em>System.Speech.dll</em>, en el espacio de nombres <a href="http://msdn.microsoft.com/es-es/library/system.speech.synthesis.aspx">System.Speech.Systhesis</a> tenemos el tipo <a href="http://msdn.microsoft.com/es-es/library/system.speech.synthesis.speechsynthesizer.aspx">SpeechSynthesizer</a>.</p>
<p>Lo único que tenemos que hacer es crear una instancia de este tipo y llamar a su método <a href="http://msdn.microsoft.com/es-es/library/ms586891.aspx">SpeakAsync</a> pasando como parámetro la cadena que queremos que sea sintetizada por el motor de texto a voz.</p>
<p>Los sistemas operativos Windows vienen de serie con un motor de texto a voz para frases en inglés. Si quieres que el sintetizador funcione con frases en castellano tendrás que comprarte una voz en castellano. Envíame un correo si quieres que te diga dónde comprar voces en castellano.</p>
<p>A continuación la orden pero esta vez con voz sintética.</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Command;
<span class="kwrd">using</span> Digi21.DigiNG;
<span class="kwrd">using</span> Digi21.Digi3D;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;
<span class="kwrd">using</span> UtilidadesDigi;
<span class="kwrd">using</span> Digi21.DigiNG.Topology;
<span class="kwrd">using</span> Digi21.Math;
<span class="kwrd">using</span> System.Speech.Synthesis;

<span class="kwrd">namespace</span> Acme
{
    [Command(Name=<span class="str">"servicio_curvas_nivel"</span>)]
    <span class="kwrd">public</span> <span class="kwrd">class</span> ServicioCurvasNivel : Command
    {
        <span class="kwrd">public</span> ServicioCurvasNivel()
        {
            <span class="kwrd">this</span>.Initialize += <span class="kwrd">new</span> EventHandler(ServicioCurvasNivel_Initialize);
            DigiNG.EntityAdded += <span class="kwrd">new</span> EventHandler&lt;EntityAddedEventArgs&gt;(DigiNG_EntityAdded);
        }

        <span class="kwrd">void</span> DigiNG_EntityAdded(<span class="kwrd">object</span> sender, EntityAddedEventArgs e)
        {
            <span class="kwrd">if</span> (e.Entity <span class="kwrd">is</span> ReadOnlyLine &amp;&amp;
                e.Entity.TieneAlgúnCódigo(<span class="kwrd">this</span>.Args))
                ControlCalidadCurvasNivel(e.Entity <span class="kwrd">as</span> ReadOnlyLine);
        }

        <span class="kwrd">private</span> <span class="kwrd">void</span> ControlCalidadCurvasNivel(ReadOnlyLine línea)
        {
            var curvasDeNivelExistentes = from entidad <span class="kwrd">in</span> DigiNG.DrawingFile
                                          <span class="kwrd">where</span> entidad != línea
                                          <span class="kwrd">where</span> entidad.TieneAlgúnCódigo(<span class="kwrd">this</span>.Args)
                                          select entidad <span class="kwrd">as</span> ReadOnlyLine;

            var intersecciones = línea.DetectIntersections(curvasDeNivelExistentes);

            List&lt;ITask&gt; tareasAAñadir = <span class="kwrd">new</span> List&lt;ITask&gt;();
            <span class="kwrd">foreach</span> (var intersección <span class="kwrd">in</span> intersecciones)
            {
                tareasAAñadir.Add(<span class="kwrd">new</span> TaskEntityGotoPoint(
                    (Point3D)intersección.Key,
                    línea,
                    2,
                    <span class="str">"Curva de nivel que cruza con otras curvas de nivel"</span>,
                    TaskSeverity.Error,
                    DigiNG.DrawingFile.Path,
                    <span class="str">"servicio_curvas_nivel"</span>));
            }

            <span class="kwrd">foreach</span> (var tarea <span class="kwrd">in</span> tareasAAñadir)
                Digi3D.Tasks.Add(tarea);

            <span class="kwrd">if</span> (tareasAAñadir.Count != 0)
            {
                <span class="kwrd">string</span> mensaje = <span class="kwrd">string</span>.Format(
                    <span class="str">"La curva de nivel que acaba de registrar se cruza {0} veces con otra curva de nivel"</span>,
                    tareasAAñadir.Count);

                SpeechSynthesizer motorVoz = <span class="kwrd">new</span> SpeechSynthesizer();
                motorVoz.SpeakAsync(mensaje);
                DigiNG.RenderScene();
            }
        }

        <span class="kwrd">void</span> ServicioCurvasNivel_Initialize(<span class="kwrd">object</span> sender, EventArgs e)
        {
            <span class="kwrd">if</span> (<span class="kwrd">this</span>.Args.Length == 0)
            {
                Digi3D.Music(MusicType.Error);
                Digi3D.ShowBallon(
                    <span class="str">"servicio_curvas_nivel"</span>,
                    <span class="str">"No se han indicado los códigos de las curvas de nivel"</span>,
                    2,
                    BallonIcon.Error);
                Dispose();
                <span class="kwrd">return</span>;
            }

            DigiNG.Commands.Pop();
        }
    }
}
</pre>
</p>
<p><a href="http://www.screencast.com/t/BBIuf27nuHN">Aquí tienes el vídeo de esta sección de código</a></p>
<p>y para terminar, vamos a hacer que la orden no detecte como erróneas intersecciones en las que una curva de nivel continúa a otra curva de nivel (es decir, si el nodo de intersección coincide con el comienzo o final de ambas curvar) y además vamos a solucionar un problema gramatical para evitar que la voz sintética diga &#8220;&#8230; se ha cruzado una veces con&#8230;&#8221;</p>
<p>Para comprobar si una intersección se ha realizado al comienzo o al final de una determinada línea, vamos a crear un método de extensión para el tipo <em>SegmentPointer</em> que nos devolverá verdadero si se cumple esta condición, facilitando mucho la lectura del código principal</p>
<p>Aquí tienes el código definitivo:</p>
<p><!-- code formatted by http://manoli.net/csharpformat/ --></p>
<pre class="csharpcode">
<span class="kwrd">using</span> System;
<span class="kwrd">using</span> System.Collections.Generic;
<span class="kwrd">using</span> System.Linq;
<span class="kwrd">using</span> System.Text;
<span class="kwrd">using</span> Digi21.DigiNG.Plugin.Command;
<span class="kwrd">using</span> Digi21.DigiNG;
<span class="kwrd">using</span> Digi21.Digi3D;
<span class="kwrd">using</span> Digi21.DigiNG.Entities;
<span class="kwrd">using</span> UtilidadesDigi;
<span class="kwrd">using</span> Digi21.DigiNG.Topology;
<span class="kwrd">using</span> Digi21.Math;
<span class="kwrd">using</span> System.Speech.Synthesis;

<span class="kwrd">namespace</span> Acme
{
    <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> UtilidadesSegmentPointer
    {
        <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">bool</span> IntersecciónAlComienzoOFinalDeLínea(<span class="kwrd">this</span> SegmentPointer entidad)
        {
            <span class="kwrd">if</span> (entidad.FirstVertex == entidad.SecondVertex &amp;&amp;
                (entidad.FirstVertex == 0 ||
                entidad.FirstVertex == entidad.Line.Points.Count - 1))
                <span class="kwrd">return</span> <span class="kwrd">true</span>;

            <span class="kwrd">return</span> <span class="kwrd">false</span>;
        }
    }

    [Command(Name=<span class="str">"servicio_curvas_nivel"</span>)]
    <span class="kwrd">public</span> <span class="kwrd">class</span> ServicioCurvasNivel : Command
    {
        <span class="kwrd">public</span> ServicioCurvasNivel()
        {
            <span class="kwrd">this</span>.Initialize += <span class="kwrd">new</span> EventHandler(ServicioCurvasNivel_Initialize);
            DigiNG.EntityAdded += <span class="kwrd">new</span> EventHandler&lt;EntityAddedEventArgs&gt;(DigiNG_EntityAdded);
        }

        <span class="kwrd">void</span> DigiNG_EntityAdded(<span class="kwrd">object</span> sender, EntityAddedEventArgs e)
        {
            <span class="kwrd">if</span> (e.Entity <span class="kwrd">is</span> ReadOnlyLine &amp;&amp;
                e.Entity.TieneAlgúnCódigo(<span class="kwrd">this</span>.Args))
                ControlCalidadCurvasNivel(e.Entity <span class="kwrd">as</span> ReadOnlyLine);
        }

        <span class="kwrd">private</span> <span class="kwrd">void</span> ControlCalidadCurvasNivel(ReadOnlyLine línea)
        {
            var curvasDeNivelExistentes = from entidad <span class="kwrd">in</span> DigiNG.DrawingFile
                                          <span class="kwrd">where</span> entidad != línea
                                          <span class="kwrd">where</span> entidad.TieneAlgúnCódigo(<span class="kwrd">this</span>.Args)
                                          select entidad <span class="kwrd">as</span> ReadOnlyLine;

            var intersecciones = línea.DetectIntersections(curvasDeNivelExistentes).ToArray();

            <span class="kwrd">if</span> (intersecciones.Length == 1)
            {
                <span class="kwrd">if</span> (intersecciones[0].Count() == 2)
                {
                    SegmentPointer líneaA = intersecciones[0].ElementAt(0);
                    SegmentPointer líneaB = intersecciones[0].ElementAt(1);

                    <span class="kwrd">if</span> (líneaA.IntersecciónAlComienzoOFinalDeLínea() &amp;&amp;
                        líneaB.IntersecciónAlComienzoOFinalDeLínea())
                        <span class="kwrd">return</span>;
                }
            }

            List&lt;ITask&gt; tareasAAñadir = <span class="kwrd">new</span> List&lt;ITask&gt;();
            <span class="kwrd">foreach</span> (var intersección <span class="kwrd">in</span> intersecciones)
            {
                tareasAAñadir.Add(<span class="kwrd">new</span> TaskEntityGotoPoint(
                    (Point3D)intersección.Key,
                    línea,
                    2,
                    <span class="str">"Curva de nivel que cruza con otras curvas de nivel"</span>,
                    TaskSeverity.Error,
                    DigiNG.DrawingFile.Path,
                    <span class="str">"servicio_curvas_nivel"</span>));
            }

            <span class="kwrd">foreach</span> (var tarea <span class="kwrd">in</span> tareasAAñadir)
                Digi3D.Tasks.Add(tarea);

            <span class="kwrd">if</span> (tareasAAñadir.Count != 0)
            {
                <span class="kwrd">string</span> mensaje;

                <span class="kwrd">if</span>( tareasAAñadir.Count == 1 )
                    mensaje = <span class="str">"La curva de nivel que acaba de registrar se cruza una vez con otra curva de nivel"</span>;
                <span class="kwrd">else</span>
                    mensaje = <span class="kwrd">string</span>.Format(
                        <span class="str">"La curva de nivel que acaba de registrar se cruza {0} veces con otra curva de nivel"</span>,
                        tareasAAñadir.Count);

                SpeechSynthesizer motorVoz = <span class="kwrd">new</span> SpeechSynthesizer();
                motorVoz.SpeakAsync(mensaje);
                DigiNG.RenderScene();
            }
        }

        <span class="kwrd">void</span> ServicioCurvasNivel_Initialize(<span class="kwrd">object</span> sender, EventArgs e)
        {
            <span class="kwrd">if</span> (<span class="kwrd">this</span>.Args.Length == 0)
            {
                Digi3D.Music(MusicType.Error);
                Digi3D.ShowBallon(
                    <span class="str">"servicio_curvas_nivel"</span>,
                    <span class="str">"No se han indicado los códigos de las curvas de nivel"</span>,
                    2,
                    BallonIcon.Error);
                Dispose();
                <span class="kwrd">return</span>;
            }

            DigiNG.Commands.Pop();
        }
    }
}
</pre></p>
Here is no comments yet by the time  your rss reader get this, Do you want to be the first commentor? Hurry up ]]></content:encoded>
			<wfw:commentRss>http://blogdigi3d.digi21.net/?feed=rss2&#038;p=447</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

