[ { "id": "ae9e82a0a0e1eb89", "type": "tab", "label": "Cálculos de H3", "disabled": false, "info": "", "env": [] }, { "id": "f511dd2230a153e7", "type": "tab", "label": "Grafíco", "disabled": false, "info": "", "env": [] }, { "id": "230815bb0628a63e", "type": "tab", "label": "Modelo", "disabled": false, "info": "", "env": [] }, { "id": "56fb69eb622c3f3b", "type": "tab", "label": "Mapa", "disabled": false, "info": "", "env": [] }, { "id": "748b5921246ec468", "type": "postgreSQLConfig", "name": "PostGIS", "host": "10.10.5.32", "hostFieldType": "str", "port": 5432, "portFieldType": "num", "database": "postgres", "databaseFieldType": "str", "ssl": "false", "sslFieldType": "bool", "applicationName": "", "applicationNameType": "str", "max": "500", "maxFieldType": "num", "idle": "100000", "idleFieldType": "num", "connectionTimeout": "300000", "connectionTimeoutFieldType": "num", "user": "postgres", "userFieldType": "str", "password": "tfmuocdfcarvajal", "passwordFieldType": "str" }, { "id": "3afa9de4406d30d8", "type": "ui-base", "name": "My Dashboard", "path": "/dashboard", "appIcon": "", "includeClientData": true, "acceptsClientConfig": [ "ui-notification", "ui-control" ], "showPathInSidebar": false, "headerContent": "page", "navigationStyle": "default", "titleBarStyle": "default", "showReconnectNotification": true, "notificationDisplayTime": 1, "showDisconnectNotification": true, "allowInstall": false }, { "id": "c81bf3ad6297e603", "type": "ui-theme", "name": "Default Theme", "colors": { "surface": "#ffffff", "primary": "#0094CE", "bgPage": "#eeeeee", "groupBg": "#ffffff", "groupOutline": "#cccccc" }, "sizes": { "density": "default", "pagePadding": "12px", "groupGap": "12px", "groupBorderRadius": "4px", "widgetGap": "12px" } }, { "id": "b332967d63ddbdfe", "type": "ui-page", "name": "Page 1", "ui": "3afa9de4406d30d8", "path": "/page1", "icon": "home", "layout": "flex", "theme": "c81bf3ad6297e603", "breakpoints": [ { "name": "Default", "px": "0", "cols": "3" }, { "name": "Tablet", "px": "576", "cols": "6" }, { "name": "Small Desktop", "px": "768", "cols": "9" }, { "name": "Desktop", "px": "1024", "cols": "12" } ], "order": 1, "className": "", "visible": "true", "disabled": "false" }, { "id": "f8afeee042444067", "type": "ui-group", "name": "Visualizador", "page": "b332967d63ddbdfe", "width": "8", "height": "8", "order": 1, "showTitle": true, "className": "", "visible": "true", "disabled": "false", "groupType": "default" }, { "id": "3005f842c2ae482e", "type": "inject", "z": "ae9e82a0a0e1eb89", "name": "Definimos el nivel 8", "props": [ { "p": "payload" }, { "p": "nivel_hexagono", "v": "8", "vt": "num" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 130, "y": 260, "wires": [ [ "ee9152d1f76ccaac" ] ] }, { "id": "41f3663b6be9dc20", "type": "debug", "z": "ae9e82a0a0e1eb89", "name": "Resultado", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 580, "y": 260, "wires": [] }, { "id": "ee9152d1f76ccaac", "type": "function", "z": "ae9e82a0a0e1eb89", "name": "Realizamos los calculos", "func": "// Node-RED Function Node Script\n// Genera tabla de hexágonos recorribles según velocidad y tiempo\n\n// Obtener parámetros de entrada\nconst nivel_hexagono = msg.nivel_hexagono || 8; // nivel H3 por defecto\nconst velocidad_min = msg.velocidad_min || 10; // km/h mínima\nconst velocidad_max = msg.velocidad_max || 30; // km/h máxima \nconst tiempo_minutos = msg.tiempo_minutos || 20; // minutos disponibles\n\nlet h3 = h3Js\n\n// Validar que tenemos las librerías necesarias\nif (typeof h3 === 'undefined') {\n node.error(\"H3 library not available\");\n return null;\n}\n\ntry {\n // Calcular longitud del lado del hexágono en metros\n const longitud_lado = h3.getHexagonEdgeLengthAvg(nivel_hexagono, 'm');\n\n // Convertir velocidades de km/h a m/s\n const velocidad_min_ms = (velocidad_min * 1000) / 3600;\n const velocidad_max_ms = (velocidad_max * 1000) / 3600;\n\n // Convertir tiempo de minutos a segundos\n const tiempo_segundos = tiempo_minutos * 60;\n\n // Calcular distancias máximas recorribles\n const distancia_min = velocidad_min_ms * tiempo_segundos;\n const distancia_max = velocidad_max_ms * tiempo_segundos;\n\n // Calcular número de hexágonos en cada dirección\n const hexagonos_min = Math.floor(distancia_min / longitud_lado);\n const hexagonos_max = Math.floor(distancia_max / longitud_lado);\n\n // Generar tabla de resultados\n const tabla_resultados = [];\n\n for (let num_hexagonos = hexagonos_min; num_hexagonos <= hexagonos_max; num_hexagonos++) {\n const distancia_recorrida = num_hexagonos * longitud_lado;\n const velocidad_requerida = (distancia_recorrida / tiempo_segundos) * 3.6; // Convertir a km/h\n\n tabla_resultados.push({\n num_hexagonos: num_hexagonos,\n distancia_metros: Math.round(distancia_recorrida),\n distancia_km: (distancia_recorrida / 1000).toFixed(2),\n velocidad_requerida_kmh: velocidad_requerida.toFixed(2),\n nivel_h3: nivel_hexagono,\n lado_hexagono_m: Math.round(longitud_lado)\n });\n }\n\n // Preparar mensaje de salida\n msg.payload = {\n parametros: {\n nivel_hexagono: nivel_hexagono,\n velocidad_min_kmh: velocidad_min,\n velocidad_max_kmh: velocidad_max,\n tiempo_minutos: tiempo_minutos,\n lado_hexagono_metros: Math.round(longitud_lado)\n },\n tabla: tabla_resultados,\n resumen: {\n hexagonos_minimos: hexagonos_min,\n hexagonos_maximos: hexagonos_max,\n rango_hexagonos: `${hexagonos_min} - ${hexagonos_max}`,\n distancia_minima_km: (hexagonos_min * longitud_lado / 1000).toFixed(2),\n distancia_maxima_km: (hexagonos_max * longitud_lado / 1000).toFixed(2)\n }\n };\n\n // Agregar información adicional para debugging\n msg.hexagonInfo = {\n nivel: nivel_hexagono,\n longitud_lado: longitud_lado\n };\n\n return msg;\n\n} catch (error) {\n node.error(\"Error processing hexagon data: \" + error.message);\n msg.error = error.message;\n return msg;\n}", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [ { "var": "h3Js", "module": "h3-js" } ], "x": 370, "y": 260, "wires": [ [ "41f3663b6be9dc20" ] ] }, { "id": "0934bc707c9b611d", "type": "comment", "z": "ae9e82a0a0e1eb89", "name": "Cálculo de la tabla de recorrido para la grua", "info": "", "x": 210, "y": 180, "wires": [] }, { "id": "d801ff754db4d5e8", "type": "inject", "z": "ae9e82a0a0e1eb89", "name": "Definimos el nivel 7", "props": [ { "p": "payload" }, { "p": "nivel_hexagono", "v": "7", "vt": "num" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 130, "y": 220, "wires": [ [ "ee9152d1f76ccaac" ] ] }, { "id": "9ca4fbaf930ad8e1", "type": "inject", "z": "ae9e82a0a0e1eb89", "name": "Definimos el nivel 9", "props": [ { "p": "payload" }, { "p": "nivel_hexagono", "v": "9", "vt": "num" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 130, "y": 300, "wires": [ [ "ee9152d1f76ccaac" ] ] }, { "id": "ab02b07ce8843526", "type": "worldmap", "z": "f511dd2230a153e7", "name": "", "lat": "", "lon": "", "zoom": "", "layer": "", "cluster": "", "maxage": "", "usermenu": "show", "layers": "show", "panit": "false", "panlock": "false", "zoomlock": "false", "hiderightclick": "false", "coords": "false", "showgrid": "false", "showruler": "false", "allowFileDrop": "false", "path": "/worldmap", "overlist": "DR,CO,RA,DN", "maplist": "OSMG,OSMC,EsriC,EsriS,UKOS", "mapname": "", "mapurl": "", "mapopt": "", "mapwms": false, "x": 440, "y": 280, "wires": [] }, { "id": "10f4dc8ae329f33e", "type": "inject", "z": "f511dd2230a153e7", "name": "Mover mapa", "props": [ { "p": "payload.command", "v": "{\"lat\":\"40.40684\",\"lon\":\"-3.5711476\"}", "vt": "json" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "x": 290, "y": 240, "wires": [ [ "ab02b07ce8843526" ] ] }, { "id": "5a0676c5ba4e943b", "type": "inject", "z": "f511dd2230a153e7", "name": "Zoom", "props": [ { "p": "payload.command", "v": "{\"zoom\":15}", "vt": "json" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "x": 270, "y": 280, "wires": [ [ "ab02b07ce8843526" ] ] }, { "id": "a94de8711099eff7", "type": "inject", "z": "f511dd2230a153e7", "name": "House", "props": [ { "p": "payload" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "{\"name\":\"Casa\",\"icon\":\"home\",\"lat\":\"40.40412499978462\",\"lon\":\"-3.5643343002563848\"}", "payloadType": "json", "x": 270, "y": 320, "wires": [ [ "ab02b07ce8843526" ] ] }, { "id": "33cf314d5cca7423", "type": "worldmap in", "z": "f511dd2230a153e7", "name": "", "path": "/worldmap", "events": "connect,disconnect,point,layer,bounds,files,draw,other", "x": 460, "y": 760, "wires": [ [ "bae0846569936702" ] ] }, { "id": "bae0846569936702", "type": "debug", "z": "f511dd2230a153e7", "name": "debug 1", "active": false, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 610, "y": 760, "wires": [] }, { "id": "acae736839e9942c", "type": "postgresql", "z": "f511dd2230a153e7", "name": "Creación de la tabla de servicios_geo", "query": "SELECT id_servicio,\n ST_AsGeoJSON(geom)::json AS geom\nFROM servicios_geo;", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 390, "y": 60, "wires": [ [ "e0f0d5d6ac05abf7" ] ] }, { "id": "d9c3487d5a4cd9f2", "type": "inject", "z": "f511dd2230a153e7", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 110, "y": 100, "wires": [ [ "8115125206120309" ] ] }, { "id": "e0f0d5d6ac05abf7", "type": "debug", "z": "f511dd2230a153e7", "name": "debug 2", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 660, "y": 120, "wires": [] }, { "id": "b34c1a218cd45651", "type": "postgresql", "z": "f511dd2230a153e7", "name": "Consulta h3", "query": "SELECT h3_lat_lng_to_cell(POINT('37.7749, -122.4194'), 9) AS h3_index;", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 290, "y": 180, "wires": [ [ "e0f0d5d6ac05abf7" ] ] }, { "id": "b6be2c6139d939f0", "type": "postgresql", "z": "f511dd2230a153e7", "name": "Consultar solo 500", "query": "SELECT id_servicio,\n ST_AsGeoJSON(geom)::json AS geom\nFROM servicios_geo\nORDER BY id_servicio\nLIMIT 500", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 410, "y": 120, "wires": [ [ "e0f0d5d6ac05abf7" ] ] }, { "id": "9fbe677b4f48e523", "type": "postgresql", "z": "f511dd2230a153e7", "name": "Hablitar extension h3", "query": "CREATE EXTENSION h3;", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 540, "y": 200, "wires": [ [ "e0f0d5d6ac05abf7" ] ] }, { "id": "2af33006f75c5667", "type": "postgresql", "z": "f511dd2230a153e7", "name": "Consulta h3", "query": "SELECT h3_lat_lng_to_cell(\n point(ST_X(geom), ST_Y(geom)), -- lon, lat\n 7\n ) AS h3,\n COUNT(*) AS total\nFROM servicios_geo\nGROUP BY h3\nORDER BY total DESC;\n", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 310, "y": 500, "wires": [ [ "e0f0d5d6ac05abf7" ] ] }, { "id": "8115125206120309", "type": "postgresql", "z": "f511dd2230a153e7", "name": "Consulta h3", "query": "SELECT h3_lat_lng_to_cell(\n point(ST_Y(geom), ST_X(geom)), -- lat, lon\n 7\n ) AS h3,\n COUNT(*) AS total\nFROM servicios_geo\nGROUP BY h3\nORDER BY total DESC;", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 310, "y": 560, "wires": [ [ "e0f0d5d6ac05abf7" ] ] }, { "id": "1b3c9cf08f33d013", "type": "postgresql", "z": "f511dd2230a153e7", "name": "Consulta h3 con geojson", "query": "SELECT \n h3_lat_lng_to_cell(point(ST_Y(geom), ST_X(geom)), 8) AS h3,\n COUNT(*) AS total\nFROM servicios_geo\nGROUP BY h3;", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 350, "y": 620, "wires": [ [ "307dd85fbc2e3dc9" ] ] }, { "id": "77fb49d72430c2b0", "type": "inject", "z": "f511dd2230a153e7", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 130, "y": 620, "wires": [ [ "1b3c9cf08f33d013" ] ] }, { "id": "b660069d0a42ba72", "type": "debug", "z": "f511dd2230a153e7", "name": "GEOJSON", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 710, "y": 580, "wires": [] }, { "id": "fa52e6e1059e9acc", "type": "worldmap", "z": "f511dd2230a153e7", "name": "", "lat": "40.41341559118809", "lon": "-3.695097056880839", "zoom": "9", "layer": "OSMC", "cluster": "", "maxage": "", "usermenu": "show", "layers": "show", "panit": "false", "panlock": "false", "zoomlock": "false", "hiderightclick": "false", "coords": "none", "showgrid": "false", "showruler": "false", "allowFileDrop": "false", "path": "/worldmap", "overlist": "", "maplist": "OSMG,OSMC,EsriT", "mapname": "", "mapurl": "", "mapopt": "", "mapwms": false, "x": 700, "y": 620, "wires": [] }, { "id": "307dd85fbc2e3dc9", "type": "function", "z": "f511dd2230a153e7", "name": "Pruebas", "func": "let h3 = h3Js;\n\n// Validar que tenemos las librerías necesarias\nif (typeof h3 === 'undefined') {\n node.error(\"H3 library not available\");\n return null;\n}\n\nconst payload = msg.payload;\n\n// Obtener el total máximo para calcular el porcentaje\nconst maxTotal = Math.max(...payload.map(i => parseFloat(i.total)));\n\nconst features = [];\n\n// --- COLORES SEGÚN PORCENTAJE ---\nfunction getFillColor(pct) {\n if (pct <= 25) return \"#00ff00\"; // verde\n if (pct <= 50) return \"#ffff00\"; // amarillo\n if (pct <= 75) return \"#ffa500\"; // naranja\n return \"#ff0000\"; // rojo\n}\n\npayload.forEach(item => {\n const h3Index = item.h3;\n const total = parseFloat(item.total);\n\n try {\n const hexBoundary = h3.cellToBoundary(h3Index, true);\n const center = h3.cellToLatLng(h3Index);\n\n // Calcular porcentaje respecto al máximo\n const pct = maxTotal > 0 ? (total / maxTotal) * 100 : 0;\n\n const fillColor = getFillColor(pct);\n\n const feature = {\n \"type\": \"Feature\",\n \"properties\": {\n \"h3\": h3Index,\n \"total\": total,\n \"pct\": pct.toFixed(2),\n \"name\": `H3: ${h3Index} - ${pct.toFixed(1)}%`,\n \"resolution\": h3.getResolution(h3Index),\n \"lat\": center[0],\n \"lon\": center[1],\n\n // ESTILO\n \"fill\": fillColor,\n \"fill-opacity\": 0.5,\n \"stroke\": \"#000000\",\n \"stroke-width\": 1,\n \"stroke-opacity\": 0.9\n },\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [hexBoundary]\n }\n };\n\n features.push(feature);\n\n } catch (error) {\n node.warn(`Error procesando H3 index ${h3Index}: ${error}`);\n }\n});\n\n// FeatureCollection final\nmsg.payload = {\n \"type\": \"FeatureCollection\",\n \"features\": features,\n \"Layer\": \"H3 Layer\"\n};\n\nreturn msg;\n", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [ { "var": "h3Js", "module": "h3-js" } ], "x": 540, "y": 620, "wires": [ [ "fa52e6e1059e9acc", "b660069d0a42ba72" ] ] }, { "id": "27eb0e7ea2d102b6", "type": "function", "z": "f511dd2230a153e7", "name": "Funciona en modo básico con colores", "func": "let h3 = h3Js;\n\n// Validar que tenemos las librerías necesarias\nif (typeof h3 === 'undefined') {\n node.error(\"H3 library not available\");\n return null;\n}\n\nconst payload = msg.payload;\nconst features = [];\n\n// --- FUNCIONES PARA ASIGNAR COLORES SEGÚN EL TOTAL ---\nfunction getFillColor(total) {\n if (total <= 10) return \"#00ff00\"; // verde\n if (total <= 50) return \"#ffff00\"; // amarillo\n if (total <= 100) return \"#ffa500\"; // naranja\n return \"#ff0000\"; // rojo\n}\n\nfunction getStrokeColor(total) {\n return \"#000000\"; // negro (puedes cambiarlo o hacerlo dinámico también)\n}\n\n// Recorrer cada elemento del array\npayload.forEach(item => {\n const h3Index = item.h3;\n const total = parseInt(item.total);\n\n try {\n // Obtener los vértices del hexágono H3\n const hexBoundary = h3.cellToBoundary(h3Index, true);\n\n // Obtener el centro del hexágono\n const center = h3.cellToLatLng(h3Index);\n\n // Colores dinámicos\n const fillColor = getFillColor(total);\n const strokeColor = getStrokeColor(total);\n\n // Crear feature GeoJSON con estilo\n const feature = {\n \"type\": \"Feature\",\n \"properties\": {\n \"h3\": h3Index,\n \"total\": total,\n \"value\": total,\n \"name\": `H3: ${h3Index} - Total: ${total}`,\n \"resolution\": h3.getResolution(h3Index),\n \"lat\": center[0],\n \"lon\": center[1],\n\n // ---- ESTILO AÑADIDO ----\n \"fill\": fillColor,\n \"fill-opacity\": 0.5, // ajustable\n \"stroke\": strokeColor,\n \"stroke-width\": 1, // ajustable\n \"stroke-opacity\": 0.9 // ajustable\n },\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": [hexBoundary]\n }\n };\n\n features.push(feature);\n\n } catch (error) {\n node.warn(`Error procesando H3 index ${h3Index}: ${error}`);\n }\n});\n\n// Crear el GeoJSON completo\nconst geojson = {\n \"type\": \"FeatureCollection\",\n \"features\": features,\n \"Layer\": \"H3 Layer\"\n};\n\nmsg.payload = geojson;\nreturn msg;\n", "outputs": 1, "timeout": 0, "noerr": 0, "initialize": "", "finalize": "", "libs": [ { "var": "h3Js", "module": "h3-js" } ], "x": 210, "y": 760, "wires": [ [] ] }, { "id": "e66c124d5be0c92e", "type": "postgresql", "z": "f511dd2230a153e7", "name": "Consultar solo 500", "query": "SELECT *\nFROM servicios_geo\nWHERE tipo_servicio != 'Base'\nLIMIT 500;", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 790, "y": 340, "wires": [ [ "95e107f031aca0fa" ] ] }, { "id": "b76d4632ea730780", "type": "inject", "z": "f511dd2230a153e7", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 610, "y": 340, "wires": [ [ "e66c124d5be0c92e" ] ] }, { "id": "95e107f031aca0fa", "type": "debug", "z": "f511dd2230a153e7", "name": "debug 3", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 960, "y": 340, "wires": [] }, { "id": "c7c7d9aedc600352", "type": "postgresql", "z": "230815bb0628a63e", "name": "Generamos la vista básica de H3 con 8", "query": "-- 1.1 Vista básica con h3 (nivel 8)\nCREATE OR REPLACE VIEW vw_servicios_h3 AS\nSELECT\n id,\n id_servicio,\n fecha_entrada,\n fecha_servicio,\n codigo_grua,\n tipo_servicio,\n geom,\n h3_lat_lng_to_cell(point(ST_Y(geom), ST_X(geom)), 8) AS h3,\n EXTRACT(HOUR FROM fecha_servicio AT TIME ZONE 'UTC')::int AS hour,\n EXTRACT(DOW FROM fecha_servicio AT TIME ZONE 'UTC')::int AS dow, -- 0 = Sunday\n CASE WHEN tipo_servicio = 'Requerimiento nulo' THEN 1 ELSE 0 END AS is_nulo\nFROM servicios_geo;", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 360, "y": 60, "wires": [ [ "eab6c7a9df1e6c8c" ] ] }, { "id": "e65ea3eb95cf6c9f", "type": "inject", "z": "230815bb0628a63e", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 110, "y": 60, "wires": [ [ "c7c7d9aedc600352" ] ] }, { "id": "eab6c7a9df1e6c8c", "type": "debug", "z": "230815bb0628a63e", "name": "debug 4", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 940, "y": 60, "wires": [] }, { "id": "a1c78ddc8ef1f1fd", "type": "postgresql", "z": "230815bb0628a63e", "name": "Generamos el nivel de demanda por hora y día de la semana", "query": "-- Conteo por (dia de la semana y hora)\nCREATE OR REPLACE VIEW vw_total_by_hour_dow AS\nSELECT\n dow,\n hour,\n COUNT(*) AS total_hour_dow\nFROM vw_servicios_h3\nGROUP BY dow, hour\nORDER BY dow, hour;\n\n--- Calcular valores para día de la semana y hora, asignamos un demand_level\nCREATE OR REPLACE VIEW vw_hour_levels_dow AS\nWITH counts AS (\n SELECT dow, hour, total_hour_dow\n FROM vw_total_by_hour_dow\n),\npercentiles_per_dow AS (\n SELECT\n dow,\n percentile_cont(0.3333) WITHIN GROUP (ORDER BY total_hour_dow) AS q1,\n percentile_cont(0.6666) WITHIN GROUP (ORDER BY total_hour_dow) AS q2\n FROM counts\n GROUP BY dow\n)\nSELECT\n c.dow,\n c.hour,\n c.total_hour_dow,\n CASE\n WHEN c.total_hour_dow <= p.q1 THEN 'baja'\n WHEN c.total_hour_dow <= p.q2 THEN 'media'\n ELSE 'alta'\n END AS demand_level\nFROM counts c\nJOIN percentiles_per_dow p ON p.dow = c.dow\nORDER BY c.dow, c.hour;", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 420, "y": 120, "wires": [ [ "e9272483eeab8c19" ] ] }, { "id": "e9272483eeab8c19", "type": "debug", "z": "230815bb0628a63e", "name": "debug 5", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 940, "y": 120, "wires": [] }, { "id": "0bec605824bbb7ea", "type": "inject", "z": "230815bb0628a63e", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 110, "y": 120, "wires": [ [ "a1c78ddc8ef1f1fd" ] ] }, { "id": "6b5cc73d3ff597e3", "type": "postgresql", "z": "230815bb0628a63e", "name": "Generamos la vista de servicios etiquetados por demanda según el horario", "query": "CREATE OR REPLACE VIEW vw_servicios_labeled AS\nSELECT s.*,\n hl.demand_level\nFROM vw_servicios_h3 s\nLEFT JOIN vw_hour_levels_dow hl\n ON s.dow = hl.dow AND s.hour = hl.hour;", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 470, "y": 180, "wires": [ [ "ae7888f46928631e" ] ] }, { "id": "b2f0b35e19e565c8", "type": "postgresql", "z": "230815bb0628a63e", "name": "Generamos la vista de demanda_h3_hour", "query": "--- Generamos la vista de la demanda h3 por hora\nCREATE OR REPLACE VIEW demanda_h3_hour AS\nSELECT\n s.h3,\n s.hour,\n s.dow,\n hl.demand_level,\n COUNT(*)::int AS total_events,\n SUM(s.is_nulo)::int AS total_nulos,\n CASE WHEN COUNT(*)=0 THEN 0.0 ELSE SUM(s.is_nulo)::double precision / COUNT(*) END AS nulo_rate\nFROM vw_servicios_labeled s\nLEFT JOIN vw_hour_levels_dow hl\n ON hl.dow = s.dow\n AND hl.hour = s.hour\nGROUP BY s.h3, s.hour, s.dow, hl.demand_level;", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 360, "y": 240, "wires": [ [ "b7999a625ea2a4f7" ] ] }, { "id": "94425a5d1997a6de", "type": "postgresql", "z": "230815bb0628a63e", "name": "Verificaciones", "query": "--- revision de valores\nSELECT * FROM vw_hour_levels_dow ORDER BY dow, hour;\n--- por servicio tiene que tener un nivel de demanda\nSELECT * FROM vw_servicios_labeled LIMIT 20;\n--- la agregación de los valores es correcta\nSELECT * FROM demanda_h3_hour ORDER BY total_events DESC LIMIT 20;", "postgreSQLConfig": "748b5921246ec468", "split": false, "rowsPerMsg": 1, "outputs": 1, "x": 280, "y": 300, "wires": [ [ "f11aaa2122049f7e" ] ] }, { "id": "6181e6dc62c93438", "type": "inject", "z": "230815bb0628a63e", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 110, "y": 180, "wires": [ [ "6b5cc73d3ff597e3" ] ] }, { "id": "01d5f95df74d1a4b", "type": "inject", "z": "230815bb0628a63e", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 110, "y": 240, "wires": [ [ "b2f0b35e19e565c8" ] ] }, { "id": "1c5af8b39a382a49", "type": "inject", "z": "230815bb0628a63e", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 110, "y": 300, "wires": [ [ "94425a5d1997a6de" ] ] }, { "id": "ae7888f46928631e", "type": "debug", "z": "230815bb0628a63e", "name": "debug 6", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 940, "y": 180, "wires": [] }, { "id": "b7999a625ea2a4f7", "type": "debug", "z": "230815bb0628a63e", "name": "debug 7", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 940, "y": 240, "wires": [] }, { "id": "f11aaa2122049f7e", "type": "debug", "z": "230815bb0628a63e", "name": "debug 8", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "true", "targetType": "full", "statusVal": "", "statusType": "auto", "x": 940, "y": 300, "wires": [] }, { "id": "1a960e62640178c4", "type": "inject", "z": "230815bb0628a63e", "name": "", "props": [ { "p": "payload" }, { "p": "topic", "vt": "str" } ], "repeat": "", "crontab": "", "once": false, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 200, "y": 380, "wires": [ [ "239bb9ceb6c8f1a2" ] ] }, { "id": "239bb9ceb6c8f1a2", "type": "http request", "z": "230815bb0628a63e", "name": "", "method": "GET", "ret": "txt", "paytoqs": "ignore", "url": "http://10.10.14.70:5000/predict?hour=11&dow=1&total_events=3", "tls": "", "persist": false, "proxy": "", "insecureHTTPParser": false, "authType": "", "senderr": false, "headers": [], "x": 360, "y": 380, "wires": [ [ "3bbe9a72ae47d0d3" ] ] }, { "id": "3bbe9a72ae47d0d3", "type": "debug", "z": "230815bb0628a63e", "name": "debug 9", "active": true, "tosidebar": true, "console": false, "tostatus": false, "complete": "false", "statusVal": "", "statusType": "auto", "x": 520, "y": 380, "wires": [] }, { "id": "cee897cd4088d738", "type": "ui-template", "z": "56fb69eb622c3f3b", "group": "f8afeee042444067", "page": "", "ui": "", "name": "Mapa Embebido", "order": 1, "width": 0, "height": 0, "head": "", "format": "", "storeOutMessages": true, "passthru": true, "resendOnRefresh": true, "templateScope": "local", "className": "", "x": 120, "y": 260, "wires": [ [] ] } ]