Archivo de la categoría: Uncategorized

Haciendo que Digi3D x64 pueda controlar Google Earth

Las versiones de Digi3D.NET de 64 bits nunca se han podido comunicar con Google Earth.

Ayer por la mañana me propuse solucionar el problema. Ha sido un día entero de frustración que te explico aquí. Hoy por la mañana por fin he dado con la solución.

Si quieres ordenar a Google Earth que haga cosas, como por ejemplo que desplace la cámara a una determinada posición, tienes que hacerlo mediante Component Object Model.

Hacer eso es trivial, primero necesitas tener acceso a los tipos que enumera el servidor COM, Visual Studio te lo facilita con la directiva #import.

Lo que no soluciona Visual Studio de forma automática es el problema que se presenta en Digi3D x64 al conectarse con el servidor COM. El problema es el siguiente:

Google Earth es una aplicación de 32 bits y se comporta como un servidor COM de tipo Inproc Server

Lo importante aquí es que se comporta como un Inproc Server, y eso significa que no deja de ser una DLL que se adjunta al proceso de la aplicación que la instancia (vamos que forma parte del mismo proceso). Si una aplicación es de 64 bits, las DLLs que cargue tienen que ser de 64 bits, no puede cargar una de 32 bits, independientemente de cómo se cargue esa DLL (LoadLibrary que sería la forma normal o mediante CoCreateInstance que es el método que se utiliza con COM).

La solución sería instalar una versión de 64 bits de Google Earth para que Digi3D.NET de 64 bits si que pueda localizar el servidor COM de 64 bits, pero no pierdas el tiempo (ya lo he perdido yo por ti), no existe una versión de 64 bits de Google Earth.

La única solución es hacer que Google Earth sea un Out-of-proc Server, y eso significa que el servidor COM sea un ejecutable y no una DLL. Cuando instancias un Out-of-proc Server, Windows se encarga de todo lo necesario para poder comunicar la aplicación cliente de 64 bits con el servidor de 32. En este caso es posible porque son dos procesos distintos, por lo tanto el servidor ya no forma parte del cliente, y por lo tanto existen mecanismos de comunicación entre procesos que compatibilizan ambas plataformas.

Como no puedo modificar Google Earth para que sea un Out-of-proc server (porque si tuviera el código fuente de Google Earth obviamente lo compilaría en 64 bits y me olvidaría del problema), no queda más remedio que desarrollar un intermediario (que llamaré Proxy) entre Digi3D.NET y Google Earth.

Este proxy va a ser un servidor COM Out-of-proc y va a implementar un interfaz con los métodos que requiere Digi3D.NET para poder ordenar a Google Earth a que se desplace a unas determinadas coordenadas.

Puedes descargar el código fuente del proyecto de nuestro repositorio de código fuente en nuestro repositorio de código fuente GitHub,  Básicamente creamos el servidor como un proyecto de tipo ATL, y luego hacemos una aplicación cliente que nos va a servir a comprobar que funciona.

…y como una imagen vale más que mil palabras, aquí tienes el vídeo en el que desarrollo esa solución. Espero que te guste.

[youtube:https://youtu.be/6KSbIZUgJVs%5D

Nueva orientación absoluta serializada como una cadena Well Known Text

Tal y como te explico en el post Novedades en el cálculo de orientaciones absolutas en el blog de novedades de Digi3D.NET, ahora el programa realiza el cálculo de las orientaciones absolutas en un sistema intermedio.

La decisión de en qué sistema se calcula la orientación explicada en el enlace anterior. Lo que te voy a explicar aquí es cómo se almacenan en el archivo .abs.xml las operaciones matemáticas necesarias para transformar un punto Terreno a Local y de Local a Modelo.

Una opción hubiera sido no almacenar en el archivo .abs.xml ninguna transformación, sino haber almacenado simplemente una cadena WKT con la definición del sistema de coordenadas de referencia de los puntos de apoyo por un lado y del sistema intermedio por otro y que luego la orientación absoluta al cargarse en memoria crease las transformaciones necesarias en función de esos datos, pero esa solución no es válida, pues no existen sistemas de coordenadas de referencia topocéntricos. Puedes crear un sistema topocéntrico en cualquier sitio, con origen en cualquier coordenada, de modo que hay infinitos sistemas topocéntricos y lógicamente no aparece ninguna en la base de datos EPSG.

De modo que la solución no es almacenar en el archivo de orientación el sistema de coordenadas de referencia intermedio, sino almacenar la serie de operaciones necesarias para transformar del sistema de coordenadas de referencia de los puntos de apoyo (que siempre será conocido, aunque sea local, pero es conocido) al sistema intemedio, que puede ser o no uno estándar, nos da igual, porque no nos interesa almacenar el sistema, sino las operaciones para llegar a él, y lo mismo del sistema intermedio al sistema modelo.

Podríamos haber almacenado estas transformaciones en un formato inventado, pero Digi3D.NET implementa el estándar Coordinate Transformation Services del Open Geospatial Consortium y eso significa que Digi3D.NET puede crear una cadena de texto Well Known Text a partir de una serie de transformaciones y al revés, puede crear en memoria una serie de transformaciones a partir de la información de una cadena Well Known Text.

Las operaciones matemáticas definidas en el estándar Coordinate Transformation Services son muy restrictivas, y todas ellas cumplen una serie de propiedades:

  1. Las operaciones que trabajan con coordenadas geográficas, siempre esperan recibir primero una longitud y luego la latitud, nunca al revés.
  2. Las operaciones que trabajan con coordenadas geográficas, siempre reciben las coordenadas en grados sexagesimales, y devuelves sexagesimales. Nunca reciben ni radianes ni centesimales.
  3. Cada operación informa del número coordenadas que recibe y del número de coordenadas que devuelve.

    Hay operaciones que reciben una coordenada y devuelven otra, como por ejemplo la operación vertical_offset que recibe una coordenada Z y devuelve otra coordenada Z, hay otras operaciones que reciben tres coordenadas y devuelven una, como por ejemplo la operación Geographic3DToGravityRelatedHeightEGM, que recibe una longitud/latitud/altitud y que devuelven una coordenada Z ortométrica, y así sucesivamente.

No se pueden pasar tres coordenadas a una operación que espera recibir dos, pues se lanzará una excepción. Digi3D.NET tiene en cuenta todos estos factores, y eso se ve reflejado en la cadena que aparece en el archivo .abs.xml>.

El estándar Coordinate Transformation Services da nombres a las distintas operaciones, ya hemos visto alguna, como vertical_offset o Geographic3DToGravityRelatedHeightEGM. Hay muchas, algunas para realizar operaciones de proyecciones de coordenadas, como transverse_mercator, cassini_soldner, … y existen dos en particular que utiliza mucho Digi3D.NET para resolver el problema de llamar a una función que recibe dos coordenadas cuando tengo tres por ejemplo.

Affine

Esta operación básicamente crea una transformación afín a partir de una matriz. Si el número de dimensiones de entrada es M y el número de dimensiones de salida es N, entonces la matriz tendrá un tamaño de [N+1][M+1]. El +1 en las dimensiones de la matriz permiten a la matriz realizar un desplazamiento además de una rotación.

Podemos utilizar una afín para mover una coordenada de posición. Digi3D.NET en ocasiones decide que tiene que cambiar el orden de la coordenada Z de X,Y,Z a Z,X,Y. Para ello utiliza la siguiente matriz:

0 0 1 0
1 0 0 0
0 1 0 0
0 0 0 1

Y al revés, transformar de Z,X,Y a X,Y,Z utilizando la siguiente matriz:

0 1 0 0
0 0 1 0
1 0 0 0
0 0 0 1

La orientación afín nos permite cambiar de dimensiones, de manera que si queremos una matriz que transforme las coordenadas X,Y,Z en X,Y, es decir, eliminar una dimensión, podemos aplicar la siguiente afín:

1 0 0 0
0 1 0 0
0 0 0 1

o justo lo contrario, en ocasiones, Digi3D.NET tiene que duplicar alguna coordenada, como por ejemplo pasar de X,Y,Z a X,Y,X,Y,Z mediante la siguiente matriz:

1 0 0 0
0 1 0 0
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1

Passthrough

Esta operación permite ejecutar una operación matemática sobre un subconjunto de coordenadas de entre las que recibe como parámetros.

Esta operación matemática recibe dos parámetros:

  1. El número de coordenadas a saltar de entre las coordenadas recibidas para hacer la transformación.
  2. Una operación matemática a realizar con el resto de coordenadas recibidas para hacer la transformación.

Con un ejemplo lo vas a entender mejor:

Supongamos que tenemos las coordenadas X,Y,Z y queremos llamar a la operación matemática lambert_to_elipsoid (que debe recibir únicamente las coordenadas X,Y y devuelve lontigud, latitud.

No podemos llamar a lambert_to_elipsoid y pasarle nuestras coordenadas X,Y,Z, pues esta operación matemática lanzará una excepción si le pasamos tres parámetros. Recibe 2 y devuelve 2. No es posible saltarse esta restricción.

Si Digi3D.NET se encuentra con este caso realizará las siguientes operaciones:

  1. Convertir la coordenada X,Y,Z en Z,X,Y (mediante una afín de las que hemos visto en el punto anterior).
  2. Llamar a la operación PASSTHROUGH(1, lambert_to_elipsoid). Esto devolverá Z,longitud,latitud.
  3. Convertir la coordenada Z,longitud,latitud en longitud,latitud,Z (mediante una afín de las que hemos visto en el punto anterior).

En ocasiones hay que transformar la coordenada Z (por ejemplo transformarla de elipsoidal a ortométrica). Existen dos tipos de operaciones matemáticas en lo referente a la coordenada Z:

  1. Las que reciben una coordenada Z y devuelven una coordenada Z’
  2. Las que reciben tres coordenadas X,Y,Z y devuelven una coordenada Z’.

El primer caso es trivial: si tenemos X,Y,Z y queremos X,Y,Z’, tan solo tenemos que llamar a la operación matemática que transforma la coordenada Z en Z’ mediante un PASSTHROUGH(2, operación_matemática), pero el otro caso requiere duplicar las coordenadas XY mediante el siguiente algoritmo:

  1. Convertimos X,Y,Z en X,Y,X,Y,Z mediante una operación afín.
  2. Llamamos a la operación que convierte de X,Y,Z a Z’ mediante un PASSTHROUGH(2, operación matemática)

A continuación tienes el contenido de una orientación absoluta hipotética de un sensor satelital en el que las coordenadas de los puntos de apoyo están en el sistema de coordenadas de referencia Merchich / Sud Maroc + EGM2008 geoid height y las coordenadas del sensor obviamente en WGS 84 3D.

<?xml version='1.0' encoding='ISO-8859-1'?>
<absolute xmlns='http://schemas.digi21.net/Digi3D/AbsoluteOrientation/v1.0' translationX='14013454.46506193800000000000' translationY='-1477144.60782388760000000000' translationZ='9031256.06252797320000000000' controlPointCoordinateSystem='COMPD_CS["Merchich / Sud Maroc + EGM2008 geoid height",PROJCS["Merchich / Sud Maroc",GEOGCS["Merchich",DATUM["Merchich",SPHEROID["Clarke 1880 (IGN)",6378249.2,293.4660212936269,AUTHORITY["EPSG","7011"]],AUTHORITY["EPSG","6261"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["°",0.01745329251994328,AUTHORITY["EPSG","9122"]],AXIS["Lat",North],AXIS["Long",East],AUTHORITY["EPSG","4261"]],PROJECTION["Lambert_Conformal_Conic_1SP"],PARAMETER["latitude_of_origin",29.69999999999997],PARAMETER["central_meridian",-5.399999999999994],PARAMETER["scale_factor",0.9996155960000001],PARAMETER["false_easting",500000],PARAMETER["false_northing",300000],PARAMETER["semi_major",6378249.2],PARAMETER["semi_minor",6356515],UNIT["metros",1,AUTHORITY["EPSG","9001"]],AXIS["X",East],AXIS["Y",North],AUTHORITY["EPSG","26192"]],VERT_CS["EGM2008 geoid height",VERT_DATUM["EGM2008 geoid",2005,AUTHORITY["EPSG","1027"]],UNIT["metros",1,AUTHORITY["EPSG","9001"]],AXIS["H",Up],AUTHORITY["EPSG","3855"]]]' mathTransformGroundToLocal='CONCAT_MT[PARAM_MT["Affine", PARAMETER["num_row",4], PARAMETER["num_col", 4],PARAMETER["elt_0_0", 0],PARAMETER["elt_0_1", 0],PARAMETER["elt_0_2", 1],PARAMETER["elt_0_3", 0],PARAMETER["elt_1_0", 1],PARAMETER["elt_1_1", 0],PARAMETER["elt_1_2", 0],PARAMETER["elt_1_3", 0],PARAMETER["elt_2_0", 0],PARAMETER["elt_2_1", 1],PARAMETER["elt_2_2", 0],PARAMETER["elt_2_3", 0],PARAMETER["elt_3_0", 0],PARAMETER["elt_3_1", 0],PARAMETER["elt_3_2", 0],PARAMETER["elt_3_3", 1]],PASSTHROUGH_MT[1,CONCAT_MT[INVERSE_MT[PARAM_MT["Lambert_Conformal_Conic_1SP",PARAMETER["latitude_of_origin",29.69999999999997],PARAMETER["central_meridian",-5.399999999999994],PARAMETER["scale_factor",0.9996155960000001],PARAMETER["false_easting",500000],PARAMETER["false_northing",300000],PARAMETER["semi_major",6378249.2],PARAMETER["semi_minor",6356515]]],PARAM_MT["Affine", PARAMETER["num_row",3], PARAMETER["num_col", 3],PARAMETER["elt_0_0", 0],PARAMETER["elt_0_1", 1],PARAMETER["elt_0_2", 0],PARAMETER["elt_1_0", 1],PARAMETER["elt_1_1", 0],PARAMETER["elt_1_2", 0],PARAMETER["elt_2_0", 0],PARAMETER["elt_2_1", 0],PARAMETER["elt_2_2", 1]]]],PARAM_MT["Affine", PARAMETER["num_row",4], PARAMETER["num_col", 4],PARAMETER["elt_0_0", 0],PARAMETER["elt_0_1", 1],PARAMETER["elt_0_2", 0],PARAMETER["elt_0_3", 0],PARAMETER["elt_1_0", 0],PARAMETER["elt_1_1", 0],PARAMETER["elt_1_2", 1],PARAMETER["elt_1_3", 0],PARAMETER["elt_2_0", 1],PARAMETER["elt_2_1", 0],PARAMETER["elt_2_2", 0],PARAMETER["elt_2_3", 0],PARAMETER["elt_3_0", 0],PARAMETER["elt_3_1", 0],PARAMETER["elt_3_2", 0],PARAMETER["elt_3_3", 1]],PARAM_MT["Affine", PARAMETER["num_row",4], PARAMETER["num_col", 4],PARAMETER["elt_0_0", 0],PARAMETER["elt_0_1", 1],PARAMETER["elt_0_2", 0],PARAMETER["elt_0_3", 0],PARAMETER["elt_1_0", 1],PARAMETER["elt_1_1", 0],PARAMETER["elt_1_2", 0],PARAMETER["elt_1_3", 0],PARAMETER["elt_2_0", 0],PARAMETER["elt_2_1", 0],PARAMETER["elt_2_2", 1],PARAMETER["elt_2_3", 0],PARAMETER["elt_3_0", 0],PARAMETER["elt_3_1", 0],PARAMETER["elt_3_2", 0],PARAMETER["elt_3_3", 1]],PASSTHROUGH_MT[2,INVERSE_MT[PARAM_MT["vertical_offset", PARAMETER["vertical_offset",0.000000]]]],PARAM_MT["Ellipsoid_To_Geocentric", PARAMETER["semi_major",6378249.2], PARAMETER["semi_minor", 6356515]]]' mathTransformLocalToGround='CONCAT_MT[PARAM_MT["Geocentric_To_Ellipsoid", PARAMETER["semi_major",6378249.2], PARAMETER["semi_minor", 6356515]],PASSTHROUGH_MT[2,PARAM_MT["vertical_offset", PARAMETER["vertical_offset",0.000000]]],PARAM_MT["Affine", PARAMETER["num_row",4], PARAMETER["num_col", 4],PARAMETER["elt_0_0", 0],PARAMETER["elt_0_1", 1],PARAMETER["elt_0_2", 0],PARAMETER["elt_0_3", 0],PARAMETER["elt_1_0", 1],PARAMETER["elt_1_1", 0],PARAMETER["elt_1_2", 0],PARAMETER["elt_1_3", 0],PARAMETER["elt_2_0", 0],PARAMETER["elt_2_1", 0],PARAMETER["elt_2_2", 1],PARAMETER["elt_2_3", 0],PARAMETER["elt_3_0", 0],PARAMETER["elt_3_1", 0],PARAMETER["elt_3_2", 0],PARAMETER["elt_3_3", 1]],PARAM_MT["Affine", PARAMETER["num_row",4], PARAMETER["num_col", 4],PARAMETER["elt_0_0", 0],PARAMETER["elt_0_1", 0],PARAMETER["elt_0_2", 1],PARAMETER["elt_0_3", 0],PARAMETER["elt_1_0", 1],PARAMETER["elt_1_1", 0],PARAMETER["elt_1_2", 0],PARAMETER["elt_1_3", 0],PARAMETER["elt_2_0", 0],PARAMETER["elt_2_1", 1],PARAMETER["elt_2_2", 0],PARAMETER["elt_2_3", 0],PARAMETER["elt_3_0", 0],PARAMETER["elt_3_1", 0],PARAMETER["elt_3_2", 0],PARAMETER["elt_3_3", 1]],PASSTHROUGH_MT[1,CONCAT_MT[PARAM_MT["Affine", PARAMETER["num_row",3], PARAMETER["num_col", 3],PARAMETER["elt_0_0", 0],PARAMETER["elt_0_1", 1],PARAMETER["elt_0_2", 0],PARAMETER["elt_1_0", 1],PARAMETER["elt_1_1", 0],PARAMETER["elt_1_2", 0],PARAMETER["elt_2_0", 0],PARAMETER["elt_2_1", 0],PARAMETER["elt_2_2", 1]],PARAM_MT["Lambert_Conformal_Conic_1SP",PARAMETER["latitude_of_origin",29.69999999999997],PARAMETER["central_meridian",-5.399999999999994],PARAMETER["scale_factor",0.9996155960000001],PARAMETER["false_easting",500000],PARAMETER["false_northing",300000],PARAMETER["semi_major",6378249.2],PARAMETER["semi_minor",6356515]]]],PARAM_MT["Affine", PARAMETER["num_row",4], PARAMETER["num_col", 4],PARAMETER["elt_0_0", 0],PARAMETER["elt_0_1", 1],PARAMETER["elt_0_2", 0],PARAMETER["elt_0_3", 0],PARAMETER["elt_1_0", 0],PARAMETER["elt_1_1", 0],PARAMETER["elt_1_2", 1],PARAMETER["elt_1_3", 0],PARAMETER["elt_2_0", 1],PARAMETER["elt_2_1", 0],PARAMETER["elt_2_2", 0],PARAMETER["elt_2_3", 0],PARAMETER["elt_3_0", 0],PARAMETER["elt_3_1", 0],PARAMETER["elt_3_2", 0],PARAMETER["elt_3_3", 1]]]' mathTransformModelToLocal='CONCAT_MT[PARAM_MT["Affine", PARAMETER["num_row",4], PARAMETER["num_col", 4],PARAMETER["elt_0_0", 0],PARAMETER["elt_0_1", 1],PARAMETER["elt_0_2", 0],PARAMETER["elt_0_3", 0],PARAMETER["elt_1_0", 1],PARAMETER["elt_1_1", 0],PARAMETER["elt_1_2", 0],PARAMETER["elt_1_3", 0],PARAMETER["elt_2_0", 0],PARAMETER["elt_2_1", 0],PARAMETER["elt_2_2", 1],PARAMETER["elt_2_3", 0],PARAMETER["elt_3_0", 0],PARAMETER["elt_3_1", 0],PARAMETER["elt_3_2", 0],PARAMETER["elt_3_3", 1]],PARAM_MT["Affine", PARAMETER["num_row",4], PARAMETER["num_col", 4],PARAMETER["elt_0_0", 1],PARAMETER["elt_0_1", 0],PARAMETER["elt_0_2", 0],PARAMETER["elt_0_3", 0],PARAMETER["elt_1_0", 0],PARAMETER["elt_1_1", 1],PARAMETER["elt_1_2", 0],PARAMETER["elt_1_3", 0],PARAMETER["elt_2_0", 0],PARAMETER["elt_2_1", 0],PARAMETER["elt_2_2", 1],PARAMETER["elt_2_3", 0],PARAMETER["elt_3_0", 0],PARAMETER["elt_3_1", 0],PARAMETER["elt_3_2", 0],PARAMETER["elt_3_3", 1]],PARAM_MT["Ellipsoid_To_Geocentric", PARAMETER["semi_major",6378137], PARAMETER["semi_minor", 6356752.314245179]]]' mathTransformLocalToModel='CONCAT_MT[PARAM_MT["Geocentric_To_Ellipsoid", PARAMETER["semi_major",6378137], PARAMETER["semi_minor", 6356752.314245179]],PARAM_MT["Affine", PARAMETER["num_row",4], PARAMETER["num_col", 4],PARAMETER["elt_0_0", 1],PARAMETER["elt_0_1", 0],PARAMETER["elt_0_2", 0],PARAMETER["elt_0_3", 0],PARAMETER["elt_1_0", 0],PARAMETER["elt_1_1", 1],PARAMETER["elt_1_2", 0],PARAMETER["elt_1_3", 0],PARAMETER["elt_2_0", 0],PARAMETER["elt_2_1", 0],PARAMETER["elt_2_2", 1],PARAMETER["elt_2_3", 0],PARAMETER["elt_3_0", 0],PARAMETER["elt_3_1", 0],PARAMETER["elt_3_2", 0],PARAMETER["elt_3_3", 1]],PARAM_MT["Affine", PARAMETER["num_row",4], PARAMETER["num_col", 4],PARAMETER["elt_0_0", 0],PARAMETER["elt_0_1", 1],PARAMETER["elt_0_2", 0],PARAMETER["elt_0_3", 0],PARAMETER["elt_1_0", 1],PARAMETER["elt_1_1", 0],PARAMETER["elt_1_2", 0],PARAMETER["elt_1_3", 0],PARAMETER["elt_2_0", 0],PARAMETER["elt_2_1", 0],PARAMETER["elt_2_2", 1],PARAMETER["elt_2_3", 0],PARAMETER["elt_3_0", 0],PARAMETER["elt_3_1", 0],PARAMETER["elt_3_2", 0],PARAMETER["elt_3_3", 1]]]'>
    <matrix omega='3.56928506875955740000' phi='-6.94752310306991740000' kappa='-156.36142599315596000000' scale='1.62608831756480400000'>
        <m row='0' col='0'>-0.76951445000824426000</m>
        <m row='0' col='1'>-0.62731999435274888000</m>
        <m row='0' col='2'>-0.11965423483427851000</m>
        <m row='1' col='0'>0.62927342674164544000</m>
        <m row='1' col='1'>-0.77676685562447978000</m>
        <m row='1' col='2'>0.02545989788050484500</m>
        <m row='2' col='0'>-0.10891494674899546000</m>
        <m row='2' col='1'>-0.05570347106353324600</m>
        <m row='2' col='2'>0.99248912220040875000</m>
    </matrix>
    <points mp='122.183975'>
        <point id='PC101' useXY='1' useZ='1'>
            <ground>
                <x>460320.184000</x>
                <y>626570.439000</y>
                <z>10.627000</z>
            </ground>
            <model>
                <x>-25.93169811989125200000</x>
                <y>32.57041742280095800000</y>
                <z>107.54832105804235000000</z>
            </model>
            <residual>
                <x>-164.295424</x>
                <y>52.099229</y>
                <z>-0.240689</z>
            </residual>
        </point>
        <point id='PC102' useXY='1' useZ='1'>
            <ground>
                <x>457916.886000</x>
                <y>624247.954000</y>
                <z>12.069000</z>
            </ground>
            <model>
                <x>-25.94416234828861400000</x>
                <y>32.58570876952164500000</y>
                <z>107.54832105804235000000</z>
            </model>
            <residual>
                <x>-28.990795</x>
                <y>-74.345370</y>
                <z>0.679271</z>
            </residual>
        </point>
        <point id='PC103' useXY='1' useZ='1'>
            <ground>
                <x>459179.334000</x>
                <y>623462.274000</y>
                <z>20.536000</z>
            </ground>
            <model>
                <x>-25.94941176839595100000</x>
                <y>32.57867438360941700000</y>
                <z>107.54837954837430000000</z>
            </model>
            <residual>
                <x>106.274481</x>
                <y>109.752252</y>
                <z>-0.886566</z>
            </residual>
        </point>
        <point id='PC104' useXY='1' useZ='1'>
            <ground>
                <x>463822.512000</x>
                <y>626685.132000</y>
                <z>10.102000</z>
            </ground>
            <model>
                <x>-25.92991593921098900000</x>
                <y>32.55051554021122200000</y>
                <z>117.54652568697929000000</z>
            </model>
            <residual>
                <x>87.007349</x>
                <y>-87.509945</y>
                <z>0.453673</z>
            </residual>
        </point>
    </points>
</absolute>