The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "TFG \u2014 Localizaci\u00f3n Visual Campus Tafira",
"nodes": [
{
"id": "note-0001",
"name": "Sticky Note \u2014 Recibir Foto",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
100,
200
],
"parameters": {
"content": "## \ud83d\udce5 Recibir Foto\nWebhook que escucha `POST /localizar`.\n\nLa API Python ya proces\u00f3 la foto antes de llegar aqu\u00ed:\n- `body.foto_b64` \u2192 imagen en base64\n- `body.embedding` \u2192 vector DINOv2 de 1536 dimensiones\n\nEste nodo es la entrada del pipeline.",
"width": 280,
"height": 160
}
},
{
"id": "note-0002",
"name": "Sticky Note \u2014 Buscar Visual",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
340,
200
],
"parameters": {
"content": "## \ud83d\udd0d Buscar Visual\nLlama a `POST /buscar_visual`.\n\nEnv\u00eda el `embedding` a **Qdrant**, que busca las 5 celdas m\u00e1s similares por distancia coseno.\n\nDevuelve: lista de `candidatos` con `celda_id`, `orientacion` y `similitud` (0\u20131).",
"width": 280,
"height": 160
}
},
{
"id": "note-0003",
"name": "Sticky Note \u2014 Detectar Elementos",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
580,
200
],
"parameters": {
"content": "## \ud83c\udfaf Detectar Elementos\nLlama a `POST /detectar_elementos`.\n\nEnv\u00eda `foto_b64`. Internamente usa:\n- **YOLO11x** \u2192 detecta objetos est\u00e1ticos: sem\u00e1foros, se\u00f1ales de stop, bocas de incendio, bancos, parqu\u00edmetros\n\nDevuelve: lista de `elementos` detectados con su posici\u00f3n y distancia estimada.",
"width": 280,
"height": 170
}
},
{
"id": "note-0005",
"name": "Sticky Note \u2014 Subinterpolacion",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
820,
200
],
"parameters": {
"content": "## \ud83d\udcd0 Subinterpolaci\u00f3n (top-K)\nLlama a `POST /subinterpolacion`.\n\nSe le pasan **los 5 candidatos top-K** del nodo anterior. El endpoint itera por todos, consulta BD4 para cada uno y consolida los desplazamientos con mediana ponderada por similitud.\n\nResultado: estimaci\u00f3n robusta de la celda real del usuario aunque ninguna celda de referencia coincida exactamente con la suya.",
"width": 280,
"height": 180
}
},
{
"id": "note-0006",
"name": "Sticky Note \u2014 Descripcion Referencia",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1060,
200
],
"parameters": {
"content": "## \ud83d\udcd6 Descripci\u00f3n Referencia\n`GET /descripcion_referencia/{celda}/{orientacion}`\n\nConsulta **BD2** para obtener la descripci\u00f3n textual que fue guardada cuando se registr\u00f3 la foto de referencia de esa celda.\n\nUsada como contexto adicional para gemma4:26b.",
"width": 280,
"height": 155
}
},
{
"id": "note-0009",
"name": "Sticky Note \u2014 Descripcion Gemma4",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1300,
200
],
"parameters": {
"content": "## \ud83e\udd16 Descripci\u00f3n Gemma4\nLlama a `POST /generar_descripcion`.\n\nEl modelo **gemma4:26b** (multimodal MoE, corre en Ollama) recibe la foto y el contexto (celda estimada, confianza, descripci\u00f3n de referencia) y genera una **descripci\u00f3n en lenguaje natural** del lugar donde est\u00e1 el usuario.",
"width": 280,
"height": 165
}
},
{
"id": "note-0010",
"name": "Sticky Note \u2014 Generar Mapa",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1540,
200
],
"parameters": {
"content": "## \ud83d\uddfa\ufe0f Generar Mapa\nLlama a `POST /generar_mapa`.\n\nCrea un mapa **HTML interactivo** con Folium + OpenStreetMap:\n- Celda estimada \u2192 pol\u00edgono + marcador **azul** (\u00fanica capa)\n\nDevuelve el HTML como string en `mapa_html`.",
"width": 280,
"height": 155
}
},
{
"id": "note-0011",
"name": "Sticky Note \u2014 Preparar Respuesta",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1780,
200
],
"parameters": {
"content": "## \ud83e\udde9 Preparar Respuesta\nNodo de c\u00f3digo JavaScript.\n\nRe\u00fane los resultados de todos los nodos anteriores y construye el **JSON final** con:\n`celda_estimada`, `confianza_vpr`, `confianza_bd4`, `desplazamiento_m`, `respuesta` (texto gemma4), `mapa_html`, `candidatas`, `razonamiento`.",
"width": 280,
"height": 170
}
},
{
"id": "note-0012",
"name": "Sticky Note \u2014 Responder",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
2020,
200
],
"parameters": {
"content": "## \ud83d\udce4 Responder\nDevuelve el JSON ensamblado al webhook de **FastAPI** que estaba esperando la respuesta.\n\nEste es el \u00faltimo nodo del pipeline. FastAPI recibe el resultado y lo env\u00eda al navegador del usuario.",
"width": 280,
"height": 140
}
},
{
"id": "a1b2c3d4-0001-0001-0001-000000000001",
"name": "Recibir Foto",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1.1,
"position": [
200,
400
],
"parameters": {
"httpMethod": "POST",
"path": "localizar",
"responseMode": "lastNode",
"options": {}
}
},
{
"id": "a1b2c3d4-0002-0002-0002-000000000002",
"name": "Buscar Visual",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
440,
400
],
"parameters": {
"method": "POST",
"url": "http://api:8000/buscar_visual",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify({ embedding: $json.body.embedding, n_resultados: 5 }) }}"
}
},
{
"id": "a1b2c3d4-0003-0003-0003-000000000003",
"name": "Detectar Elementos",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
680,
400
],
"parameters": {
"method": "POST",
"url": "http://api:8000/detectar_elementos",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify({ foto_b64: $('Recibir Foto').item.json.body.foto_b64 }) }}"
}
},
{
"id": "a1b2c3d4-0005-0005-0005-000000000005",
"name": "Subinterpolacion",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
920,
400
],
"parameters": {
"method": "POST",
"url": "http://api:8000/subinterpolacion",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify({ celda_ref: $('Buscar Visual').item.json.candidatos[0].celda_id, orientacion: $('Buscar Visual').item.json.candidatos[0].orientacion, elementos_usuario: $('Detectar Elementos').item.json.elementos, similitud: $('Buscar Visual').item.json.candidatos[0].similitud, candidatos: $('Buscar Visual').item.json.candidatos, focal_usuario: $('Recibir Foto').item.json.body.focal_usuario }) }}"
}
},
{
"id": "a1b2c3d4-0006-0006-0006-000000000006",
"name": "Descripcion Referencia",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1160,
400
],
"parameters": {
"method": "GET",
"url": "={{ 'http://api:8000/descripcion_referencia/' + $('Buscar Visual').item.json.candidatos[0].celda_id + '/' + $('Buscar Visual').item.json.candidatos[0].orientacion }}"
}
},
{
"id": "a1b2c3d4-0009-0009-0009-000000000009",
"name": "Descripcion Gemma4",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1400,
400
],
"parameters": {
"method": "POST",
"url": "http://api:8000/generar_descripcion",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify({ foto_b64: $('Recibir Foto').item.json.body.foto_b64, celda_estimada: $('Subinterpolacion').item.json.celda_estimada, celda_ref: $('Buscar Visual').item.json.candidatos[0].celda_id, desplazamiento_m: $('Subinterpolacion').item.json.desplazamiento_m || 0, confianza_vpr: $('Buscar Visual').item.json.candidatos[0].similitud, confianza_bd4: $('Subinterpolacion').item.json.confianza_bd4 || 0, descripcion_lugar: $('Descripcion Referencia').item.json.descripcion || '' }) }}"
}
},
{
"id": "a1b2c3d4-0010-0010-0010-000000000010",
"name": "Generar Mapa",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1640,
400
],
"parameters": {
"method": "POST",
"url": "http://api:8000/generar_mapa",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"contentType": "raw",
"rawContentType": "application/json",
"body": "={{ JSON.stringify({ celda_estimada: $('Subinterpolacion').item.json.celda_estimada, celda_ref: $('Buscar Visual').item.json.candidatos[0].celda_id, candidatas: $('Buscar Visual').item.json.candidatos.map(c => c.celda_id) }) }}"
}
},
{
"id": "a1b2c3d4-0011-0011-0011-000000000011",
"name": "Preparar Respuesta",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1880,
400
],
"parameters": {
"jsCode": "const buscarVisual = $('Buscar Visual').item.json;\nconst subinterp = $('Subinterpolacion').item.json;\nconst descGemma4 = $('Descripcion Gemma4').item.json;\nconst mapa = $('Generar Mapa').item.json;\n\nconst top = buscarVisual.candidatos[0];\n\nreturn [{\n json: {\n celda_estimada : subinterp.celda_estimada || top.celda_id,\n celda_ref : top.celda_id,\n orientacion : top.orientacion,\n confianza_vpr : top.similitud,\n confianza_bd4 : subinterp.confianza_bd4 || 0,\n desplazamiento_m : subinterp.desplazamiento_m || 0,\n respuesta : descGemma4.descripcion || '',\n mapa_html : mapa.mapa_html || '',\n candidatas : buscarVisual.candidatos.map(c => c.celda_id),\n detalle_bd4 : subinterp.detalle || [],\n razonamiento : top.similitud >= 0.5 ? 'Alta confianza VPR' : 'Baja confianza VPR \u2014 resultado aproximado',\n fuera_de_campus : buscarVisual.fuera_de_campus || false,\n similitud_top : top.similitud\n }\n}];"
}
},
{
"id": "a1b2c3d4-0012-0012-0012-000000000012",
"name": "Responder",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
2120,
400
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}",
"options": {
"responseHeaders": {
"entries": [
{
"name": "Content-Type",
"value": "application/json"
}
]
}
}
}
}
],
"connections": {
"Recibir Foto": {
"main": [
[
{
"node": "Buscar Visual",
"type": "main",
"index": 0
}
]
]
},
"Buscar Visual": {
"main": [
[
{
"node": "Detectar Elementos",
"type": "main",
"index": 0
}
]
]
},
"Detectar Elementos": {
"main": [
[
{
"node": "Subinterpolacion",
"type": "main",
"index": 0
}
]
]
},
"Subinterpolacion": {
"main": [
[
{
"node": "Descripcion Referencia",
"type": "main",
"index": 0
}
]
]
},
"Descripcion Referencia": {
"main": [
[
{
"node": "Descripcion Gemma4",
"type": "main",
"index": 0
}
]
]
},
"Descripcion Gemma4": {
"main": [
[
{
"node": "Generar Mapa",
"type": "main",
"index": 0
}
]
]
},
"Generar Mapa": {
"main": [
[
{
"node": "Preparar Respuesta",
"type": "main",
"index": 0
}
]
]
},
"Preparar Respuesta": {
"main": [
[
{
"node": "Responder",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1",
"saveManualExecutions": true,
"callerPolicy": "workflowsFromSameOwner",
"errorWorkflow": ""
},
"meta": {
"templateCredsSetupCompleted": true
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
TFG — Localización Visual Campus Tafira. Uses httpRequest. Webhook trigger; 18 nodes.
Source: https://github.com/angeelrivero/TFG/blob/a3fc75a16e5bf09da2e496ec52b9f163bd81503e/n8n_workflowMV.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This n8n template provides enterprise-level version control for your workflows using GitHub integration. Stop losing hours to broken workflows and manual exports – get proper commit history, visual di
This flow creates dummy files for every item added in your *Arrs (Radarr/Sonarr) with the tag .
eek-Go v2 (Batch-Then-Review). Uses httpRequest. Webhook trigger; 75 nodes.
This workflow receives webhook requests from a content calendar and uses the X API v2 to publish text posts, threads, image/video posts, and polls, as well as delete existing posts and run a credentia
This workflow acts as a central API gateway for all technical indicator agents in the Binance Spot Market Quant AI system. It listens for incoming webhook requests and dynamically routes them to the c