{"id":11737,"date":"2026-04-03T09:26:59","date_gmt":"2026-04-03T07:26:59","guid":{"rendered":"https:\/\/bitacora.eniac2000.com\/?p=11737"},"modified":"2026-04-03T09:27:31","modified_gmt":"2026-04-03T07:27:31","slug":"aplicacion-android-para-fotogrametria-con-ia-fase-4-integracion-cloud-exportacion-kmz-y-cierre-del-ciclo-completo","status":"publish","type":"post","link":"https:\/\/bitacora.eniac2000.com\/?p=11737","title":{"rendered":"Aplicaci\u00f3n Android para fotogrametr\u00eda con IA. Fase 4: Integraci\u00f3n cloud, exportaci\u00f3n KMZ y cierre del ciclo completo"},"content":{"rendered":"<div class=\"seriesmeta\">Esta entrada es la parte 6 de 6 de la serie <a href=\"https:\/\/bitacora.eniac2000.com\/?series=fotogrametria-asistida-por-ia\" class=\"series-1957\" title=\"Fotogrametr\u00eda asistida por IA\">Fotogrametr\u00eda asistida por IA<\/a><\/div>\n<p>En los art\u00edculos anteriores de esta serie describ\u00ed c\u00f3mo la aplicaci\u00f3n planifica misiones de fotogrametr\u00eda, hace volar al dron de forma aut\u00f3noma y supervisa el vuelo en tiempo real. Al terminar la <a href=\"https:\/\/bitacora.eniac2000.com\/?p=11696\" target=\"_blank\" rel=\"noreferrer noopener\">Fase 2<\/a> y la <a href=\"https:\/\/bitacora.eniac2000.com\/?p=11696\" target=\"_blank\" rel=\"noreferrer noopener\">Fase 3<\/a>, la app era ya capaz de ejecutar una misi\u00f3n completa y tomar las fotos en los waypoints correctos, pero el resultado quedaba a medias: las im\u00e1genes se almacenaban en la tarjeta del dron, y llegar desde ah\u00ed hasta un modelo 3D procesado requer\u00eda transferirlas manualmente al ordenador, subirlas a WebODM, esperar el procesado y abrir el resultado en el navegador. La Fase 4 cierra ese hueco: conecta la app directamente con el servidor de procesado y a\u00f1ade la exportaci\u00f3n de la telemetr\u00eda de vuelo en formato KMZ.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">El pipeline completo: del dron al servidor en tres pasos<\/h2>\n\n\n\n<p>El objetivo de esta fase era que, al terminar un vuelo, el operador pudiera pulsar un bot\u00f3n y que la app se encargara de todo lo dem\u00e1s: descargar las fotos del dron al tel\u00e9fono, subirlas al servidor WebODM y monitorizar el procesado hasta que el modelo estuviera listo. El flujo qued\u00f3 as\u00ed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Dron (tarjeta SD)\n     \u2502 DJI MSDK v5 \u2014 MediaDownloader\n     \u25bc\nTel\u00e9fono (cach\u00e9 local)\n     \u2502 WorkManager \u2014 OdmUploadWorker\n     \u25bc\nWebODM API REST\n     \u2502 WorkManager \u2014 OdmPollingWorker\n     \u25bc\nProcesado ODM en servidor\n     \u2502 status == COMPLETED\n     \u25bc\nNotificaci\u00f3n al usuario<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"587\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/fotogrametria-automatica-webODM-1024x587.png\" alt=\"Primera construcci\u00f3n fotogram\u00e9trica automatizada desde la aplicaci\u00f3n\" class=\"wp-image-11738\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/fotogrametria-automatica-webODM-1024x587.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/fotogrametria-automatica-webODM-300x172.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/fotogrametria-automatica-webODM-768x440.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/fotogrametria-automatica-webODM.png 1409w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Primera construcci\u00f3n fotogram\u00e9trica automatizada desde la aplicaci\u00f3n<\/figcaption><\/figure>\n\n\n\n<p>Cada etapa tiene su propia pieza t\u00e9cnica, y cada una trajo sus propios <em>bugs<\/em>. Voy por partes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Descargando las fotos del dron<\/h2>\n\n\n\n<p>El primer paso era recuperar las im\u00e1genes capturadas durante el vuelo. El DJI Mobile SDK v5 expone una API de gesti\u00f3n de medios que permite listar y descargar los ficheros almacenados en la tarjeta del dron. Se implement\u00f3 en <code>MediaDownloader.kt<\/code>, que recorre los ficheros disponibles, filtra los JPEG y los descarga al almacenamiento local de la app.<\/p>\n\n\n\n<p>El primer bug apareci\u00f3 aqu\u00ed, y fue de esos que son dif\u00edciles de detectar a simple vista. Las fotos se descargaban correctamente \u2014sin errores, sin excepciones\u2014 pero pesaban todas exactamente 976 bytes, lo que indicaba que estaban claramente corruptas.<\/p>\n\n\n\n<p>La causa estaba en la API de descarga del SDK. El listener de descarga recibe dos par\u00e1metros: <code>data: ByteArray<\/code> (el chunk de datos) y <code>position: Long<\/code>. El nombre <code>position<\/code> sugiere tama\u00f1o, pero en realidad es el <strong><em>offset<\/em> acumulado<\/strong> desde el inicio del fichero. El c\u00f3digo original usaba <code>position.toInt()<\/code> como n\u00famero de bytes a escribir, que en el primer <em>chunk<\/em> es el tama\u00f1o de ese <em>chunk<\/em>, pero en los siguientes es el <em>offset<\/em> total acumulado, lo que provocaba escrituras truncadas o desbordadas. La soluci\u00f3n es usar <code>data.size<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Incorrecto: position es el offset acumulado, no el tama\u00f1o del chunk\nbos.write(data, 0, position.toInt())\n\n\/\/ Correcto: data.size es el tama\u00f1o real del chunk recibido\nbos.write(data, 0, data.size)<\/code><\/pre>\n\n\n\n<p>Un detalle de la API del SDK que no aparece documentado con claridad, y que s\u00f3lo se descubre comparando el tama\u00f1o de los ficheros descargados con los originales.<\/p>\n\n\n\n<p>Adem\u00e1s de la descarga, se a\u00f1adi\u00f3 un sistema de <strong>cach\u00e9 local<\/strong>: si la app detecta que ya existen fotos v\u00e1lidas (m\u00e1s de 10 KB, extensi\u00f3n JPEG) en el directorio de la misi\u00f3n, las reutiliza directamente sin necesidad de reconectar el dron. Esto permite corregir par\u00e1metros de procesado en WebODM y resubir las mismas im\u00e1genes sin volver a conectar el hardware.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Subiendo a WebODM con WorkManager<\/h2>\n\n\n\n<p>La carga de las im\u00e1genes al servidor se implement\u00f3 con <strong>WorkManager<\/strong>, que es la herramienta correcta para operaciones de red largas en Android: sobrevive a la muerte de la Activity, gestiona reintentos autom\u00e1ticos y permite encadenar <em>workers<\/em> pasando datos entre ellos.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"461\" height=\"1024\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-000402-461x1024.png\" alt=\"Ortofotograf\u00eda sobre WebODM\" class=\"wp-image-11735\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-000402-461x1024.png 461w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-000402-135x300.png 135w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-000402-768x1707.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-000402-691x1536.png 691w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-000402-922x2048.png 922w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-000402-1024x2276.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-000402.png 1080w\" sizes=\"auto, (max-width: 461px) 100vw, 461px\" \/><figcaption class=\"wp-element-caption\">Ortofotograf\u00eda sobre WebODM<\/figcaption><\/figure>\n\n\n\n<p>La cadena es <code>OdmUploadWorker<\/code> \u2192 <code>OdmPollingWorker<\/code>: el primero crea el proyecto en WebODM, sube todas las im\u00e1genes y devuelve el <code>task_id<\/code>; el segundo recoge ese ID y hace <em>polling<\/em> peri\u00f3dico del estado del procesado, actualizando la pantalla hasta que el status pasa a <code>COMPLETED<\/code> o <code>FAILED<\/code>.<\/p>\n\n\n\n<p>La autenticaci\u00f3n con WebODM usa JWT, implementada como un interceptor de OkHttp (<code>JwtAuthInterceptor<\/code>) que a\u00f1ade<em> <\/em>la cabecera <code>Authorization: JWT &lt;token><\/code> a todas las peticiones y la renueva autom\u00e1ticamente cuando caduca. Aqu\u00ed apareci\u00f3 el segundo <em>bug<\/em> relevante: WebODM devuelve <strong>403<\/strong>, no 401, cuando el <em>token<\/em> ha expirado. El interceptor inicial s\u00f3lo manejaba 401, as\u00ed que los <em>tokens<\/em> caducados provocaban fallos silenciosos en las peticiones. La soluci\u00f3n fue a\u00f1adir 403 a la l\u00f3gica de refresco.<\/p>\n\n\n\n<p>El tercer <em>bug<\/em> vino de una asunci\u00f3n incorrecta sobre la API de WebODM. El c\u00f3digo esperaba que los IDs de tarea fueran enteros, igual que los IDs de proyecto. WebODM usa <strong>UUIDs<\/strong> (strings) para los task IDs. El <em>crash<\/em> era un <code>NumberFormatException<\/code> al parsear la respuesta, y la soluci\u00f3n fue cambiar <code>taskId: Int<\/code> a <code>taskId: String<\/code> en toda la cadena: modelos, API, repositorio, workers y ViewModel.<\/p>\n\n\n\n<p>Las credenciales del servidor (URL, usuario y contrase\u00f1a) se configuran desde una pantalla de ajustes dedicada y se almacenan con <code>EncryptedSharedPreferences<\/code>, nunca en texto claro. Una pantalla de procesamiento muestra en tiempo real el progreso de descarga, upload y procesado en servidor mediante barras de progreso independientes.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"461\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-081852-1024x461.png\" alt=\"Flujo de procesado completo, desde el dron al procesado\" class=\"wp-image-11732\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-081852-1024x461.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-081852-300x135.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-081852-768x346.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-081852-1536x691.png 1536w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-081852-2048x922.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Flujo de procesado completo, desde el dron al procesado<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Exportaci\u00f3n KMZ: un problema de arquitectura disfrazado de <em>feature<\/em><\/h2>\n\n\n\n<p>La exportaci\u00f3n KMZ \u2014un fichero que contiene el pol\u00edgono de la misi\u00f3n, la trayectoria real del dron y los puntos donde se tomaron las fotos\u2014 parec\u00eda una funcionalidad sencilla. Result\u00f3 ser el problema t\u00e9cnico m\u00e1s interesante de toda la fase.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"461\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-085038-1024x461.png\" alt=\"Men\u00fa de exportaci\u00f3n de misiones, tanto ODM como KMZ\" class=\"wp-image-11736\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-085038-1024x461.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-085038-300x135.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-085038-768x346.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-085038-1536x691.png 1536w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/Screenshot_20260403-085038-2048x922.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Men\u00fa de exportaci\u00f3n de misiones, tanto ODM como KMZ<\/figcaption><\/figure>\n\n\n\n<p>El KMZ generaba correctamente el pol\u00edgono del \u00e1rea planificada, pero la trayectoria y los puntos de captura sal\u00edan vac\u00edos. El motivo: esos datos viven en <code>MissionExecutor<\/code> durante el vuelo, pero cuando el usuario termina la misi\u00f3n y vuelve a la pantalla del mapa para exportar el KMZ, Navigation Compose ya ha destruido y recreado la pantalla, y los datos se han perdido.<\/p>\n\n\n\n<p>Se intentaron cinco aproximaciones distintas para pasar esos datos al ViewModel al volver de la pantalla de ejecuci\u00f3n:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><code>LaunchedEffect(Unit)<\/code> en MapScreen \u2014 solo ejecuta una vez al montar la pantalla, no al volver de otra.<\/li>\n\n\n\n<li><code>DisposableEffect<\/code> con <code>ON_RESUME<\/code> \u2014 en navegaci\u00f3n intra-Activity de Compose, el evento de resume no se dispara como se esperar\u00eda.<\/li>\n\n\n\n<li>Un flag en <code>savedStateHandle<\/code> \u2014 demasiado fr\u00e1gil, con problemas de timing.<\/li>\n\n\n\n<li><code>LaunchedEffect(MissionExecutor.missionEndTime)<\/code> \u2014 una <code>var<\/code> normal no es Compose State, no garantiza recomposici\u00f3n.<\/li>\n\n\n\n<li>Eliminar el <code>reset()<\/code> de <code>MissionExecutor<\/code> al pulsar atr\u00e1s \u2014 los datos se perd\u00edan igualmente por timing entre la navegaci\u00f3n y la lectura.<\/li>\n<\/ol>\n\n\n\n<p>Cinco intentos fallidos. La soluci\u00f3n lleg\u00f3 al plantear el problema de otra manera: en lugar de intentar pasar datos a trav\u00e9s del flujo de navegaci\u00f3n, <strong>escribirlos a disco durante el vuelo<\/strong> y leerlos desde ah\u00ed cuando haga falta.<\/p>\n\n\n\n<p>Se cre\u00f3 <code>FlightDataWriter<\/code>, un componente que graba la telemetr\u00eda estructurada en un fichero JSON independiente mientras el dron vuela. La arquitectura es limpia:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MissionExecutor \u2500\u2500\u2500 FlightDataWriter.start()\n     \u2502                       \u2502\n     \u251c\u2500\u2500 recordTrailPoint() \u2500\u2500\u25ba addTrailPoint(lat, lng, alt)\n     \u251c\u2500\u2500 recordPhotoPoint() \u2500\u2500\u25ba addPhotoSnapshot(...)\n     \u2502                       \u2502\n     \u2514\u2500\u2500 RTH\/finish \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25ba FlightDataWriter.finish()\n                                        \u2502\n                                        \u25bc\n                              telemetry_&lt;timestamp&gt;.json\n\nMapViewModel.exportKml() \u25c4\u2500\u2500 FlightDataWriter.loadForMission()<\/code><\/pre>\n\n\n\n<p>El fichero se escribe durante el vuelo, de forma completamente independiente a la navegaci\u00f3n entre pantallas. Cuando el usuario exporta el KMZ horas o d\u00edas despu\u00e9s, <code>FlightDataWriter.loadForMission()<\/code> recupera los datos desde el fichero. La navegaci\u00f3n Compose ya no importa.<\/p>\n\n\n\n<p>El patr\u00f3n de escribir a fichero en lugar de depender de la memoria de la aplicaci\u00f3n era ya conocido en el proyecto: el <code>FlightLogger<\/code> de diagn\u00f3stico que se implement\u00f3 en la Fase 2 para poder revisar logs en campo sin acceso a ADB sigue exactamente la misma filosof\u00eda.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">El bug del <em>Locale<\/em>: comas que rompen coordenadas<\/h2>\n\n\n\n<p>Con los datos de telemetr\u00eda disponibles, el KMZ generaba la trayectoria y los puntos de foto. Pero al abrirlo en Google Earth, la trayectoria mostraba saltos de miles de kil\u00f3metros y altitudes absurdas.<\/p>\n\n\n\n<p>La causa era sutil. El formato KML representa las coordenadas como <code>longitud,latitud,altitud<\/code> separados por comas. En un dispositivo Android con el idioma configurado en espa\u00f1ol, <code>\"%.1f\".format(6.3)<\/code> produce <code>\"6,3\"<\/code> con coma decimal en lugar de punto. En el contexto del KML, esa coma adicional se interpreta como un separador de coordenadas:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Generado (Locale espa\u00f1ol):  -6,041619,37,4422,6,3    \u2190 6 valores (corrupto)\nEsperado:                   -6.041619,37.4422,6.3     \u2190 3 valores (correcto)<\/code><\/pre>\n\n\n\n<p>Cada coordenada en el fichero era un sinsentido geogr\u00e1fico. La soluci\u00f3n es forzar <code>Locale.US<\/code> en todos los formatos num\u00e9ricos del KML, garantizando el punto como separador decimal independientemente de la configuraci\u00f3n del dispositivo:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>String.format(Locale.US, \"%.8f,%.8f,%.1f\", longitude, latitude, altitude)<\/code><\/pre>\n\n\n\n<p>Es el tipo de <em>bug<\/em> que no aparece en los tests porque los tests suelen ejecutarse en m\u00e1quinas con <em>Locale<\/em> ingl\u00e9s, y que en producci\u00f3n solo se manifiesta en dispositivos configurados con idiomas que usan coma decimal. Cl\u00e1sico.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"666\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/registro-vuelo-google-earth-pro-1024x666.png\" alt=\"Vuelo exportado en KMZ visualizado en Google Earth Pro. Se observan el \u00e1rea de la misi\u00f3n de vuelo, el vuelo en s\u00ed, y los PDIs con fotograf\u00edas\" class=\"wp-image-11731\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/registro-vuelo-google-earth-pro-1024x666.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/registro-vuelo-google-earth-pro-300x195.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/registro-vuelo-google-earth-pro-768x500.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2026\/04\/registro-vuelo-google-earth-pro.png 1374w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Vuelo exportado en KMZ visualizado en Google Earth Pro. Se observan el \u00e1rea de la misi\u00f3n de vuelo, el vuelo en s\u00ed, y los PDIs con fotograf\u00edas<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">El estado del proyecto tras la Fase 4<\/h2>\n\n\n\n<p>Con esta fase cerrada, el ciclo completo de fotogrametr\u00eda est\u00e1 cubierto de extremo a extremo dentro de la app:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Planificar<\/strong>: dibujar el \u00e1rea sobre el mapa, configurar altura y overlaps, previsualizar la grilla de vuelo.<\/li>\n\n\n\n<li><strong>Volar<\/strong>: ejecuci\u00f3n aut\u00f3noma via Virtual Stick con control de heading y disparo autom\u00e1tico de c\u00e1mara.<\/li>\n\n\n\n<li><strong>Supervisar<\/strong>: telemetr\u00eda en tiempo real, trail de trayectoria, puntos de foto sobre mini-mapa.<\/li>\n\n\n\n<li><strong>Descargar<\/strong>: transferencia de im\u00e1genes del dron al tel\u00e9fono con cach\u00e9 local.<\/li>\n\n\n\n<li><strong>Procesar<\/strong>: upload a WebODM, seguimiento del procesado en servidor, notificaci\u00f3n al completarse.<\/li>\n\n\n\n<li><strong>Exportar<\/strong>: KMZ con pol\u00edgono de misi\u00f3n, trayectoria real y puntos de captura con altitud.<\/li>\n<\/ol>\n\n\n\n<p>Lo que queda pendiente est\u00e1 documentado como Fase 5: visualizaci\u00f3n del ortomosaico resultante como <em>overlay<\/em> sobre el mapa, gesti\u00f3n de m\u00faltiples bater\u00edas para misiones largas, reanudaci\u00f3n de misi\u00f3n interrumpida, soporte para otros drones DJI, edici\u00f3n de pol\u00edgono post-confirmaci\u00f3n y cach\u00e9 <em>offline<\/em> de tiles de mapa para trabajar en campo sin cobertura de datos. Material, en definitiva, para seguir contando en pr\u00f3ximos art\u00edculos.<\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"seriesmeta\">Esta entrada es la parte 6 de 6 de la serie <a href=\"https:\/\/bitacora.eniac2000.com\/?series=fotogrametria-asistida-por-ia\" class=\"series-1957\" title=\"Fotogrametr\u00eda asistida por IA\">Fotogrametr\u00eda asistida por IA<\/a><\/div><p>En los art\u00edculos anteriores de esta serie describ\u00ed c\u00f3mo la<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1845,13],"tags":[106,1959,551,738,1961,1202],"series":[1957],"class_list":["post-11737","post","type-post","status-publish","format-standard","hentry","category-generado-con-ia","category-informatica","tag-android","tag-claude-code","tag-dron","tag-google-earth","tag-kmz","tag-open-drone-map","series-fotogrametria-asistida-por-ia"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts\/11737","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=11737"}],"version-history":[{"count":1,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts\/11737\/revisions"}],"predecessor-version":[{"id":11739,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts\/11737\/revisions\/11739"}],"wp:attachment":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=11737"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=11737"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=11737"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fseries&post=11737"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}