El mundo del modelamiento de la información (BIM) no es ajeno a los avances y cada vez mayor necesidad de manipular y controlar data para la generación de reportes inteligentes.
Hoy, más que nunca, las diferentes herramientas de gestión buscan proveer un enfoque integrador y de fácil conexión entre ellas. Ese es el caso de BIM 360, una plataforma desarrollada por Autodesk que permite elevar la productividad de un proyecto al congregar todas sus herramientas (AutoCAD, Revit, Navisworks, etc.) en un solo espacio común de interactividad, obviamente, situado en la nube (Cloud).
Más aún, Autodesk y Power BI son partners para el desarrollo de tarjetas inteligentes (Partner Cards) que permiten suministrar análisis de data en ambas direcciones. A continuación mostramos un resumen de lo que considera este proceso:
Una vez desarollado el modelo de información 3D en Autodesk Revit, se puede fácilmente exportar las tablas de información del modelo 3D al entorno PowerBI para su análisis. Para realizar este proceso, tendremos que hacer uso de la base de datos ACCESS como puente de comunicación con Power BI.
Para realizar la exportación de las tablas del modelo, se deberá primero tener instalado el componente gratuito Revit DB Link (Add-In). Este Add-In se puede descargar de forma gratuita de Autodesk Store.
Una vez instalado, en la pestaña Add-Ins damos click en Revit DB Link para seleccionar la ruta en donde queremos exportar las tablas de nuestro modelo en formato ACCESS (base de datos).
Teniendo la base de datos del modelo REVIT exportada, procedemos a abrir Power BI y a cargar las tablas. Para esto, en Power BI damos click en Get Data y seleccionamos Access Database. Seleccionamos la ruta donde anteriormente guardamos la base de datos ACCESS.
Una vez cargada la información (tablas) a Power BI, podemos realizar las visualizaciones/reportes que consideramos necesarias y publicar el reporte a Power BI Service (Cloud).
Una vez en la nube, solicitaremos a Power BI Service compartir el reporte a la web para obtener el enlace (iFrame) que utilizaremos en BIM 360. Debemos copiar este enlace para poder configurar nuestra tarjeta en BIM 360.
Una vez culminado el trabajo en Power BI, abrimos BIM 360 Insights y en nuestro dashboard seleccionamos el boton "Customise" en la franja superior derecha de la pantalla.
Seleccionamos la opción Card Library y en los filtros seleccionamos Partner Cards. A continuación, ubicamos a Power BI como partner de Autodesk.
Seleccionamos la tarjeta Power BI y notaremos que la tarjeta se adiciona al final del dashboard en BIM 360 Insights.
Procedemos a configurar nuestra tarjeta colocando un título para la misma y colocando el enlace URL que previamente copiamos de Power BI Service. Y, listo! habremos completado la integración REVIT -- > POWER BI -- > BIM 360.
... ya sé lo que piensan... ya sé.. y funciona viceversa? qué pasa si queremos llevar el modelo 3D a Power BI? No es posible? .. no se preocupen, Planicontrol (créditos a Jeancarlo Durán) te tiene cubierto (bueno al menos parcialmente).
C) BIM 360: Modelo 3D Autodesk REVIT a Power BI
Al momento no existe una comunicación directa para exportar un modelo 3D Revit y visualizarlo en Power BI, y capaz, tendríamos que preguntarnos por qué quisieramos hacer eso? BIM 360 es una plataforma completa para la colaboración eficaz de todas las herramientas Autodesk a lo largo de todas las etapas de diseño e incluso de la construcción.
La visualización y análisis de los modelos BIM debería mantenerse en esta plataforma y, más bien, añadir inteligencia de la data al incorporar análisis creados en Power BI (tal y como vimos en los puntos A y B).
Pero bien, ante la insistencia de múltiples usuarios en querer incorporar estas visualizaciones de modelos 3D a Power BI, una empresa externa desarrolló un Custom Visual (desarrollo en BI) para permitir a Power BI recibir y leer archivos IFC. No obstante, esta desarrollo es una herramienta de pago mensual suministrada por esta empresa externa.
Con la herramienta puedes seleccionar las plantas del edificio, detalles de áreas específicas o un conjunto de áreas resaltadas por colores, filtrar los espacios combinados de la estructura. El usuario puede utilizar filtros que muestran las plantas de interés y las áreas resaltadas.
En la magen debajo puedes visualizar el demo:
Agradezco como siempre que nos acompañen y tengo por seguro que el interés que percibí durante el Webinar se verá reflejado en excelentes profesionales buscando prepararse de la mejor forma para enfrentar los desafíos que nos aguardan en la Gestión y Control de Proyectos.
Por favor, no dejen de seguirnos en nuestras redes de Planicontrol, es a través de su apoyo que nos motivan a seguir compartiendo información de valor para nuestro desarrollo profesional.
************** Siguenos en nuestras redes sociales **************
Web Planicontrol: http://planicontrol.com.pe/cursos/
LinkedIn Planicontrol: Linkedin Planicontrol
YouTube Planicontrol: YouTube Planicontrol
Facebook Planicontrol: https://www.facebook.com/planicontrol/
LinkedIn Jeancarlo Duran: www.linkedin.com/in/jeancarlo-duran
Su amigo, Jeancarlo.
// Init style shamelessly stolen from jQuery http://jquery.com | |
var Froogaloop = (function(){ | |
// Define a local copy of Froogaloop | |
function Froogaloop(iframe) { | |
// The Froogaloop object is actually just the init constructor | |
return new Froogaloop.fn.init(iframe); | |
} | |
var eventCallbacks = {}, | |
hasWindowEvent = false, | |
isReady = false, | |
slice = Array.prototype.slice, | |
playerDomain = ''; | |
Froogaloop.fn = Froogaloop.prototype = { | |
element: null, | |
init: function(iframe) { | |
if (typeof iframe === "string") { | |
iframe = document.getElementById(iframe); | |
} | |
this.element = iframe; | |
// Register message event listeners | |
playerDomain = getDomainFromUrl(this.element.getAttribute('src')); | |
return this; | |
}, | |
/* | |
* Calls a function to act upon the player. | |
* | |
* @param {string} method The name of the Javascript API method to call. Eg: 'play'. | |
* @param {Array|Function} valueOrCallback params Array of parameters to pass when calling an API method | |
* or callback function when the method returns a value. | |
*/ | |
api: function(method, valueOrCallback) { | |
if (!this.element || !method) { | |
return false; | |
} | |
var self = this, | |
element = self.element, | |
target_id = element.id !== '' ? element.id : null, | |
params = !isFunction(valueOrCallback) ? valueOrCallback : null, | |
callback = isFunction(valueOrCallback) ? valueOrCallback : null; | |
// Store the callback for get functions | |
if (callback) { | |
storeCallback(method, callback, target_id); | |
} | |
postMessage(method, params, element); | |
return self; | |
}, | |
/* | |
* Registers an event listener and a callback function that gets called when the event fires. | |
* | |
* @param eventName (String): Name of the event to listen for. | |
* @param callback (Function): Function that should be called when the event fires. | |
*/ | |
addEvent: function(eventName, callback) { | |
if (!this.element) { | |
return false; | |
} | |
var self = this, | |
element = self.element, | |
target_id = element.id !== '' ? element.id : null; | |
storeCallback(eventName, callback, target_id); | |
// The ready event is not registered via postMessage. It fires regardless. | |
if (eventName != 'ready') { | |
postMessage('addEventListener', eventName, element); | |
} | |
else if (eventName == 'ready' && isReady) { | |
callback.call(null, target_id); | |
} | |
return self; | |
}, | |
/* | |
* Unregisters an event listener that gets called when the event fires. | |
* | |
* @param eventName (String): Name of the event to stop listening for. | |
*/ | |
removeEvent: function(eventName) { | |
if (!this.element) { | |
return false; | |
} | |
var self = this, | |
element = self.element, | |
target_id = element.id !== '' ? element.id : null, | |
removed = removeCallback(eventName, target_id); | |
// The ready event is not registered | |
if (eventName != 'ready' && removed) { | |
postMessage('removeEventListener', eventName, element); | |
} | |
} | |
}; | |
/** | |
* Handles posting a message to the parent window. | |
* | |
* @param method (String): name of the method to call inside the player. For api calls | |
* this is the name of the api method (api_play or api_pause) while for events this method | |
* is api_addEventListener. | |
* @param params (Object or Array): List of parameters to submit to the method. Can be either | |
* a single param or an array list of parameters. | |
* @param target (HTMLElement): Target iframe to post the message to. | |
*/ | |
function postMessage(method, params, target) { | |
if (!target.contentWindow.postMessage) { | |
return false; | |
} | |
var url = target.getAttribute('src').split('?')[0], | |
data = JSON.stringify({ | |
method: method, | |
value: params | |
}); | |
target.contentWindow.postMessage(data, url); | |
} | |
/** | |
* Event that fires whenever the window receives a message from its parent | |
* via window.postMessage. | |
*/ | |
function onMessageReceived(event) { | |
var data, method; | |
try { | |
data = JSON.parse(event.data); | |
method = data.event || data.method; | |
} | |
catch(e) { | |
//fail silently... like a ninja! | |
} | |
if (method == 'ready' && !isReady) { | |
isReady = true; | |
} | |
// Handles messages from moogaloop only | |
if (event.origin != playerDomain) { | |
return false; | |
} | |
var value = data.value, | |
eventData = data.data, | |
target_id = target_id === '' ? null : data.player_id, | |
callback = getCallback(method, target_id), | |
params = []; | |
if (!callback) { | |
return false; | |
} | |
if (value !== undefined) { | |
params.push(value); | |
} | |
if (eventData) { | |
params.push(eventData); | |
} | |
if (target_id) { | |
params.push(target_id); | |
} | |
return params.length > 0 ? callback.apply(null, params) : callback.call(); | |
} | |
/** | |
* Stores submitted callbacks for each iframe being tracked and each | |
* event for that iframe. | |
* | |
* @param eventName (String): Name of the event. Eg. api_onPlay | |
* @param callback (Function): Function that should get executed when the | |
* event is fired. | |
* @param target_id (String) [Optional]: If handling more than one iframe then | |
* it stores the different callbacks for different iframes based on the iframe's | |
* id. | |
*/ | |
function storeCallback(eventName, callback, target_id) { | |
if (target_id) { | |
if (!eventCallbacks[target_id]) { | |
eventCallbacks[target_id] = {}; | |
} | |
eventCallbacks[target_id][eventName] = callback; | |
} | |
else { | |
eventCallbacks[eventName] = callback; | |
} | |
} | |
/** | |
* Retrieves stored callbacks. | |
*/ | |
function getCallback(eventName, target_id) { | |
if (target_id) { | |
return eventCallbacks[target_id][eventName]; | |
} | |
else { | |
return eventCallbacks[eventName]; | |
} | |
} | |
function removeCallback(eventName, target_id) { | |
if (target_id && eventCallbacks[target_id]) { | |
if (!eventCallbacks[target_id][eventName]) { | |
return false; | |
} | |
eventCallbacks[target_id][eventName] = null; | |
} | |
else { | |
if (!eventCallbacks[eventName]) { | |
return false; | |
} | |
eventCallbacks[eventName] = null; | |
} | |
return true; | |
} | |
/** | |
* Returns a domain's root domain. | |
* Eg. returns http://vimeo.com when http://vimeo.com/channels is sbumitted | |
* | |
* @param url (String): Url to test against. | |
* @return url (String): Root domain of submitted url | |
*/ | |
function getDomainFromUrl(url) { | |
var url_pieces = url.split('/'), | |
domain_str = ''; | |
for(var i = 0, length = url_pieces.length; i < length; i++) { | |
if(i<3) {domain_str += url_pieces[i];} | |
else {break;} | |
if(i<2) {domain_str += '/';} | |
} | |
return domain_str; | |
} | |
function isFunction(obj) { | |
return !!(obj && obj.constructor && obj.call && obj.apply); | |
} | |
function isArray(obj) { | |
return toString.call(obj) === '[object Array]'; | |
} | |
// Give the init function the Froogaloop prototype for later instantiation | |
Froogaloop.fn.init.prototype = Froogaloop.fn; | |
// Listens for the message event. | |
// W3C | |
if (window.addEventListener) { | |
window.addEventListener('message', onMessageReceived, false); | |
} | |
// IE | |
else { | |
window.attachEvent('onmessage', onMessageReceived, false); | |
} | |
// Expose froogaloop to the global object | |
return (window.Froogaloop = window.$f = Froogaloop); | |
})(); |