Browse Source

Merged in FD3-616 (pull request #43)

Mapa de calor en CMTS y OLT (estado, rx y tx)

Approved-by: Guillermo Espinoza <guillermo@interlink.com.ar>
Approved-by: Maximiliano Schvindt <maximiliano@interlink.com.ar>
Maximiliano Schvindt 6 years ago
parent
commit
f461f1c7e2

+ 132 - 1
src/StatsBundle/Controller/StatsController.php

@@ -1201,6 +1201,10 @@ class StatsController extends Controller
         $end = microtime(true);
         $time = $end - $init;
 
+        list($cablemodems, $locations, $status) = $this->getCmtsCablemodems($cmts);
+
+        
+
         return $this->render('StatsBundle:Stats:show_cmts.html.twig', array(
             'base_template' => $adminPool->getTemplate('layout'),
             'admin_pool' => $adminPool,
@@ -1224,9 +1228,75 @@ class StatsController extends Controller
             'server' => $serverId,
             'fatherDevice' => $cmtsDeviceId,
             'fatherDeviceType' => 1,
+            'locations' => $locations,
+            'location' => $this->setDefaultMap()
         ));
     }
 
+    private function getCmtsCablemodems($cmts) {
+        
+        $cmtsDeviceId = $cmts->getDeviceId();
+        $deviceServer = $cmts->getDeviceServer()->getId();
+        
+        $em = $this->get("doctrine.orm.entity_manager");
+        $q = $em->getRepository("StatsBundle:Cablemodem")->createQueryBuilder('c');
+        $cablemodems = $q->where('c.cmtsDeviceId = :cmtsDeviceId')
+                ->andWhere('c.deviceServer = :deviceServer')
+                ->setParameter('cmtsDeviceId', $cmtsDeviceId)
+                ->setParameter('deviceServer', $deviceServer)
+                ->getQuery()
+                ->getResult();
+
+        
+        $on = $off = 0;
+        $locations = $cms = array();
+
+        foreach($cablemodems as $cm) {
+            
+            $row = array();
+            $index = $cm->getIndex();
+            $row['ip'] = $cm->getIp();
+            $row['mac'] = $mac = $cm->getMac();
+            $row['index'] = $index;
+            $row['upIf'] = $cm->getUpIf();
+            $row['downIf'] = $cm->getDownIf();
+            $row['status'] = $status = $cm->getStatus();
+            $row['txPower'] = $tx = $cm->getTxPower();
+            $row['rxPower'] = $rx = $cm->getRxPower();
+            $row['signal'] = $cm->getSignal();
+            $row['microreflection'] = $cm->getMicroreflection();
+            $row['uptime'] = $cm->getUptime();
+            if($cm->getUptime()) {
+                $uptime = explode(",",$cm->getUptime());
+                if(isset($uptime[0]))
+                    $row['uptime'] = $uptime[0];
+            }
+            $row['rxPowerCmts'] = $cm->getRxPowerCmts();
+            $row['lat'] = $lat = $cm->getLat();
+            $row['lng'] = $lng = $cm->getLng();
+            $row['inOctets'] = $cm->getInOctets();
+            $row['outOctets'] = $cm->getOutOctets();
+
+            if($status) {
+                $on++;
+            } else {
+                $off++;
+            }
+
+            $cms[$index] = $row;
+
+            if($lat && $lng && $rx) {
+                $locations[$index] = array('mac' => $mac, 'tx' => $tx, 'rx' => $rx, 'lat' => $lat, 'lng' => $lng, 'status' => $status);
+            }
+
+        }
+
+        $status = array('online' => $on, 'offline' => $off);
+
+        return array($cms, $locations, $status);
+
+    }
+
     function getInterfaceWithCer($cmtsDeviceId, $serverId) 
     {
         $em = $this->get("doctrine.orm.entity_manager");
@@ -1727,6 +1797,8 @@ class StatsController extends Controller
         $toStar = date("Y-m-d",$ts);
         $star = $this->getConsumptionFather($serverId,$oltDeviceId,2,$fromStar,$toStar);
 
+        list($onus, $locations, $status) = $this->getOltOnus($device);
+
         $end = microtime(true);
         $time = $end - $init;
 
@@ -1748,10 +1820,69 @@ class StatsController extends Controller
             'ts' => $ts,
             'server' => $serverId,
             'fatherDevice' => $oltDeviceId,
-            'fatherDeviceType' => 2
+            'fatherDeviceType' => 2,
+            'locations' => $locations,
+            'location' => $this->setDefaultMap()
         ));
     }
 
+    private function getOltOnus($olt) {
+        
+        $on = $off = 0;
+        $locations = $_onus = array();
+
+        $oltDeviceId = $olt->getDeviceId();
+        $deviceServer = $olt->getDeviceServer()->getId();
+        
+        $em = $this->get("doctrine.orm.entity_manager");
+        $q = $em->getRepository("StatsBundle:Onu")->createQueryBuilder('o');
+        $onus = $q->where('o.oltDeviceId = :oltDeviceId')
+                ->andWhere('o.deviceServer = :deviceServer')
+                ->setParameter('oltDeviceId', $oltDeviceId)
+                ->setParameter('deviceServer', $deviceServer)
+                ->getQuery()
+                ->getResult();
+
+        
+        foreach($onus as $onu) {
+            
+            $row = array();
+            $index = $onu->getIndex();
+            $row['psn'] = $psn = $onu->getPonSerialNumber();
+            $row['sn'] = $onu->getSerialNumber();
+            $row['port'] = $onu->getPonPort();
+            $row['index'] = $index;
+            $row['status'] = $status = $onu->getStatus();
+            $row['txPower'] = $tx = $onu->getTxPower();
+            $row['rxPower'] = $rx = $onu->getRxPower();
+            $row['voltage'] = $onu->getVoltage();
+            $row['temperature'] = $onu->getTemperature();
+            $row['rxPowerOlt'] = $onu->getRxPowerOlt();
+            $row['lat'] = $lat = $onu->getLat();
+            $row['lng'] = $lng = $onu->getLng();
+            $row['inOctets'] = $onu->getInOctets();
+            $row['outOctets'] = $onu->getOutOctets();
+
+            if($status) {
+                $on++;
+            } else {
+                $off++;
+            }
+
+            $_onus[$index] = $row;
+
+            if($lat && $lng) {
+                $locations[$index] = array('psn' => $psn, 'tx' => $tx, 'rx' => $rx, 'lat' => $lat, 'lng' => $lng, 'status' => $status);
+            }
+
+        }
+
+        $status = array('online' => $on, 'offline' => $off);
+
+        return array($_onus, $locations, $status);
+
+    }
+
     public function onuSignal($oltDeviceId, $serverId) 
     {
 

+ 11 - 0
src/StatsBundle/Resources/public/css/style.css

@@ -116,3 +116,14 @@ a.pickerXlsSlaButton:hover {
 input#limit_x_device_table {
     text-align: center!important;
 }
+
+#div-map-loader {
+    margin: 0 auto;
+    margin-top: 40px;
+}
+
+#div-map-loader #loader {
+    display: block!important;
+    float: none!important;
+    margin: 0 auto;
+}

+ 213 - 0
src/StatsBundle/Resources/public/js/leaflet-heatmap-src.js

@@ -0,0 +1,213 @@
+'use strict';
+
+L.HeatLayer = (L.Layer ? L.Layer : L.Class).extend({
+
+    // options: {
+    //     minOpacity: 0.05,
+    //     maxZoom: 18,
+    //     radius: 25,
+    //     blur: 15,
+    //     max: 1.0
+    // },
+
+    initialize: function (latlngs, options) {
+        this._latlngs = latlngs;
+        L.setOptions(this, options);
+    },
+
+    setLatLngs: function (latlngs) {
+        this._latlngs = latlngs;
+        return this.redraw();
+    },
+
+    addLatLng: function (latlng) {
+        this._latlngs.push(latlng);
+        return this.redraw();
+    },
+
+    setOptions: function (options) {
+        L.setOptions(this, options);
+        if (this._heat) {
+            this._updateOptions();
+        }
+        return this.redraw();
+    },
+
+    redraw: function () {
+        if (this._heat && !this._frame && this._map && !this._map._animating) {
+            this._frame = L.Util.requestAnimFrame(this._redraw, this);
+        }
+        return this;
+    },
+
+    onAdd: function (map) {
+        this._map = map;
+
+        if (!this._canvas) {
+            this._initCanvas();
+        }
+
+        if (this.options.pane) {
+            this.getPane().appendChild(this._canvas);
+        }else{
+            map._panes.overlayPane.appendChild(this._canvas);
+        }
+
+        map.on('moveend', this._reset, this);
+
+        if (map.options.zoomAnimation && L.Browser.any3d) {
+            map.on('zoomanim', this._animateZoom, this);
+        }
+
+        this._reset();
+    },
+
+    onRemove: function (map) {
+        if (this.options.pane) {
+            this.getPane().removeChild(this._canvas);
+        }else{
+            map.getPanes().overlayPane.removeChild(this._canvas);
+        }
+
+        map.off('moveend', this._reset, this);
+
+        if (map.options.zoomAnimation) {
+            map.off('zoomanim', this._animateZoom, this);
+        }
+    },
+
+    addTo: function (map) {
+        map.addLayer(this);
+        return this;
+    },
+
+    _initCanvas: function () {
+        var canvas = this._canvas = L.DomUtil.create('canvas', 'leaflet-heatmap-layer leaflet-layer');
+
+        var originProp = L.DomUtil.testProp(['transformOrigin', 'WebkitTransformOrigin', 'msTransformOrigin']);
+        canvas.style[originProp] = '50% 50%';
+
+        var size = this._map.getSize();
+        canvas.width  = size.x;
+        canvas.height = size.y;
+
+        var animated = this._map.options.zoomAnimation && L.Browser.any3d;
+        L.DomUtil.addClass(canvas, 'leaflet-zoom-' + (animated ? 'animated' : 'hide'));
+
+        this._heat = simpleheat(canvas);
+        this._updateOptions();
+    },
+
+    _updateOptions: function () {
+        this._heat.radius(this.options.radius || this._heat.defaultRadius, this.options.blur);
+
+        if (this.options.gradient) {
+            this._heat.gradient(this.options.gradient);
+        }
+        if (this.options.max) {
+            this._heat.max(this.options.max);
+        }
+    },
+
+    _reset: function () {
+        var topLeft = this._map.containerPointToLayerPoint([0, 0]);
+        L.DomUtil.setPosition(this._canvas, topLeft);
+
+        var size = this._map.getSize();
+
+        if (this._heat._width !== size.x) {
+            this._canvas.width = this._heat._width  = size.x;
+        }
+        if (this._heat._height !== size.y) {
+            this._canvas.height = this._heat._height = size.y;
+        }
+
+        this._redraw();
+    },
+
+    _redraw: function () {
+        if (!this._map) {
+            return;
+        }
+        var data = [],
+            r = this._heat._r,
+            size = this._map.getSize(),
+            bounds = new L.Bounds(
+                L.point([-r, -r]),
+                size.add([r, r])),
+
+            max = this.options.max === undefined ? 1 : this.options.max,
+            maxZoom = this.options.maxZoom === undefined ? this._map.getMaxZoom() : this.options.maxZoom,
+            v = 1 / Math.pow(2, Math.max(0, Math.min(maxZoom - this._map.getZoom(), 12))),
+            cellSize = r / 2,
+            grid = [],
+            panePos = this._map._getMapPanePos(),
+            offsetX = panePos.x % cellSize,
+            offsetY = panePos.y % cellSize,
+            i, len, p, cell, x, y, j, len2, k;
+
+        // console.time('process');
+        for (i = 0, len = this._latlngs.length; i < len; i++) {
+            p = this._map.latLngToContainerPoint(this._latlngs[i]);
+            if (bounds.contains(p)) {
+                x = Math.floor((p.x - offsetX) / cellSize) + 2;
+                y = Math.floor((p.y - offsetY) / cellSize) + 2;
+
+                var alt =
+                    this._latlngs[i].alt !== undefined ? this._latlngs[i].alt :
+                    this._latlngs[i][2] !== undefined ? +this._latlngs[i][2] : 1;
+                k = alt * v;
+
+                grid[y] = grid[y] || [];
+                cell = grid[y][x];
+
+                if (!cell) {
+                    grid[y][x] = [p.x, p.y, k];
+
+                } else {
+                    cell[0] = (cell[0] * cell[2] + p.x * k) / (cell[2] + k); // x
+                    cell[1] = (cell[1] * cell[2] + p.y * k) / (cell[2] + k); // y
+                    cell[2] += k; // cumulated intensity value
+                }
+            }
+        }
+
+        for (i = 0, len = grid.length; i < len; i++) {
+            if (grid[i]) {
+                for (j = 0, len2 = grid[i].length; j < len2; j++) {
+                    cell = grid[i][j];
+                    if (cell) {
+                        data.push([
+                            Math.round(cell[0]),
+                            Math.round(cell[1]),
+                            Math.min(cell[2], max)
+                        ]);
+                    }
+                }
+            }
+        }
+        // console.timeEnd('process');
+
+        // console.time('draw ' + data.length);
+        this._heat.data(data).draw(this.options.minOpacity);
+        // console.timeEnd('draw ' + data.length);
+
+        this._frame = null;
+    },
+
+    _animateZoom: function (e) {
+        var scale = this._map.getZoomScale(e.zoom),
+            offset = this._map._getCenterOffset(e.center)._multiplyBy(-scale).subtract(this._map._getMapPanePos());
+
+        if (L.DomUtil.setTransform) {
+            L.DomUtil.setTransform(this._canvas, offset, scale);
+
+        } else {
+            this._canvas.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(offset) + ' scale(' + scale + ')';
+        }
+    }
+});
+
+L.heatLayer = function (latlngs, options) {
+    return new L.HeatLayer(latlngs, options);
+};

+ 142 - 0
src/StatsBundle/Resources/public/js/simple-heat.js

@@ -0,0 +1,142 @@
+'use strict';
+
+if (typeof module !== 'undefined') module.exports = simpleheat;
+
+function simpleheat(canvas) {
+    if (!(this instanceof simpleheat)) return new simpleheat(canvas);
+
+    this._canvas = canvas = typeof canvas === 'string' ? document.getElementById(canvas) : canvas;
+
+    this._ctx = canvas.getContext('2d');
+    this._width = canvas.width;
+    this._height = canvas.height;
+
+    this._max = 1;
+    this._data = [];
+}
+
+simpleheat.prototype = {
+
+    defaultRadius: 25,
+
+    defaultGradient: {
+        0.4: 'blue',
+        0.6: 'cyan',
+        0.7: 'lime',
+        0.8: 'yellow',
+        1.0: 'red'
+    },
+
+    data: function (data) {
+        this._data = data;
+        return this;
+    },
+
+    max: function (max) {
+        this._max = max;
+        return this;
+    },
+
+    add: function (point) {
+        this._data.push(point);
+        return this;
+    },
+
+    clear: function () {
+        this._data = [];
+        return this;
+    },
+
+    radius: function (r, blur) {
+        blur = blur === undefined ? 15 : blur;
+
+        // create a grayscale blurred circle image that we'll use for drawing points
+        var circle = this._circle = this._createCanvas(),
+            ctx = circle.getContext('2d'),
+            r2 = this._r = r + blur;
+
+        circle.width = circle.height = r2 * 2;
+
+        ctx.shadowOffsetX = ctx.shadowOffsetY = r2 * 2;
+        ctx.shadowBlur = blur;
+        ctx.shadowColor = 'black';
+
+        ctx.beginPath();
+        ctx.arc(-r2, -r2, r, 0, Math.PI * 2, true);
+        ctx.closePath();
+        ctx.fill();
+
+        return this;
+    },
+
+    resize: function () {
+        this._width = this._canvas.width;
+        this._height = this._canvas.height;
+    },
+
+    gradient: function (grad) {
+        // create a 256x1 gradient that we'll use to turn a grayscale heatmap into a colored one
+        var canvas = this._createCanvas(),
+            ctx = canvas.getContext('2d'),
+            gradient = ctx.createLinearGradient(0, 0, 0, 256);
+
+        canvas.width = 1;
+        canvas.height = 256;
+
+        for (var i in grad) {
+            gradient.addColorStop(+i, grad[i]);
+        }
+
+        ctx.fillStyle = gradient;
+        ctx.fillRect(0, 0, 1, 256);
+
+        this._grad = ctx.getImageData(0, 0, 1, 256).data;
+
+        return this;
+    },
+
+    draw: function (minOpacity) {
+        if (!this._circle) this.radius(this.defaultRadius);
+        if (!this._grad) this.gradient(this.defaultGradient);
+
+        var ctx = this._ctx;
+
+        ctx.clearRect(0, 0, this._width, this._height);
+
+        // draw a grayscale heatmap by putting a blurred circle at each data point
+        for (var i = 0, len = this._data.length, p; i < len; i++) {
+            p = this._data[i];
+            ctx.globalAlpha = Math.min(Math.max(p[2] / this._max, minOpacity === undefined ? 0.05 : minOpacity), 1);
+            ctx.drawImage(this._circle, p[0] - this._r, p[1] - this._r);
+        }
+
+        // colorize the heatmap, using opacity value of each pixel to get the right color from our gradient
+        var colored = ctx.getImageData(0, 0, this._width, this._height);
+        this._colorize(colored.data, this._grad);
+        ctx.putImageData(colored, 0, 0);
+
+        return this;
+    },
+
+    _colorize: function (pixels, gradient) {
+        for (var i = 0, len = pixels.length, j; i < len; i += 4) {
+            j = pixels[i + 3] * 4; // get gradient color from opacity value
+
+            if (j) {
+                pixels[i] = gradient[j];
+                pixels[i + 1] = gradient[j + 1];
+                pixels[i + 2] = gradient[j + 2];
+            }
+        }
+    },
+
+    _createCanvas: function () {
+        if (typeof document !== 'undefined') {
+            return document.createElement('canvas');
+        } else {
+            // create a new canvas instance in node.js
+            // the canvas class needs to have a default constructor without any parameter
+            return new this._canvas.constructor();
+        }
+    }
+};

+ 5 - 1
src/StatsBundle/Resources/translations/StatsBundle.es.yml

@@ -408,4 +408,8 @@ Diff: Diferencia
 limit_x_device_in_gb: Límite(GB) de dispositivos, al editarlo se actualizará la tabla comparando los valores contra el límite ingresado. Presionar 'tab' para actualizar el campo.
 Limit_in_gb: Límite(GB) del perfil o valor tomado como referencia, al editarlo puede ver como se actualiza el porcentaje de uso.
 star_table_daily_header: Consumo(GB) por día
-star_table_daily_sub_header: Puede ordenarse la tabla haciendo click en la cabecera
+star_table_daily_sub_header: Puede ordenarse la tabla haciendo click en la cabecera
+Map: Mapa
+Status: Estado
+RX Power: RX Power
+TX Power: TX Power

+ 7 - 5
src/StatsBundle/Resources/views/Stats/Template/cablemodem_location.html.twig

@@ -21,13 +21,15 @@ function initializeMap() {
 
     window.map = new L.Map('map', { center: new L.LatLng({{location['lat']}}, {{location['lng']}}), zoom: {{location['zoom']}} });
 
-    var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
-    var osmAttrib = '&copy; OpenStreetMap';
-    var osm = L.tileLayer(osmUrl, { maxZoom: 18, attribution: osmAttrib });
+    var gstreet =  L.tileLayer('https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{
+                attribution: 'Google Inc.',
+                maxNativeZoom: 22,
+                maxZoom: 24,
+                subdomains:['mt0','mt1','mt2','mt3']
+    });
 
     var controlLayers = L.control.layers({
-        'OSM': osm.addTo(window.map),
-        "Google": L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', {zIndex:1, attribution: '&copy; Google'})
+        "Street": gstreet.addTo(window.map),
     }, {}, { position: 'topleft', collapsed: false });
 
     {% if location['marker'] %}

+ 142 - 0
src/StatsBundle/Resources/views/Stats/Template/cmts_cms_location.html.twig

@@ -0,0 +1,142 @@
+{# {{dump(locations)}} #}
+
+<div style="padding: 10px">
+    <div style="text-align: center">
+    <span class="no_defined nas_data_origen">{{'Cablemodems'|trans({}, 'StatsBundle') }}
+    &nbsp;&nbsp;({{locations|length}} {{ 'cablemodems_with_lat_lng'|trans({}, 'StatsBundle') }})
+    </span>
+    </div>
+    <div id="div-map-loader">{% include '@Stats/Stats/Template/load_svg.html.twig' with {'width': '120px'} %}</div>
+    <div id="map" style="height:575px;display:hidden"></div>
+</div>
+
+
+
+<script>
+
+/* --------------------- PROGRAMA PRINCIPAL ------------------------- */
+
+var elements = {{locations|json_encode|raw}};
+var loc = {{location|json_encode|raw}};
+var initializedMap = false;
+
+/* --------------------- Carga Mapa -------------------------------- */
+
+function initializeMap() {
+
+    initializedMap = true;
+    $("#div-map-loader").hide();
+    $("#map").show();
+
+    //window.map = new L.Map('map', { center: new L.LatLng({{location['lat']}}, {{location['lng']}}), zoom: {{location['zoom']}} });
+    window.map = new L.Map('map', { center: new L.LatLng(loc.lat, loc.lng), zoom: loc.zoom });
+    
+    map.invalidateSize();
+
+    var gstreet =  L.tileLayer('https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{
+                attribution: 'Google Inc.',
+                maxNativeZoom: 22,
+                maxZoom: 24,
+                subdomains:['mt0','mt1','mt2','mt3']
+            });
+
+    var controlLayers = L.control.layers({
+        "Street": gstreet.addTo(window.map),
+        "Satellite": L.tileLayer('https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',{
+            attribution: 'Google Inc.',
+            maxNativeZoom: 21,
+            maxZoom: 21,
+            subdomains:['mt0','mt1','mt2','mt3']
+        }),
+        "Hybrid": L.tileLayer('https://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}',{
+            attribution: 'Google Inc.',
+            maxNativeZoom: 21,
+            maxZoom: 21,
+            subdomains:['mt0','mt1','mt2','mt3']
+        }),
+    }, {}, {position: 'topleft',collapsed: true});
+
+    controlLayers.addTo(window.map);
+    
+    bounds = [];
+    heatOn = [];
+    heatOff = [];
+    heatRxR = [];
+    heatRxY = [];
+    heatRxG = [];
+    heatTxR = [];
+    heatTxY = [];
+    heatTxG = [];
+    
+    for(e in elements) {
+        
+        var cm = elements[e];
+
+        if(cm.status) {
+            heatOn.push([cm.lat, cm.lng, 1.0]);
+        } else {
+            heatOff.push([cm.lat, cm.lng, 1.0]);
+        }
+
+        bounds.push([cm.lat, cm.lng]);
+
+        if(cm.rx <= -12) {
+            heatRxR.push([cm.lat, cm.lng, 1.0]);
+        } else if(cm.rx > -12 && cm.rx < -10) {
+            heatRxY.push([cm.lat, cm.lng, 1.0]);
+        } else if(cm.rx > -10 && cm.rx < 10) {
+            heatRxG.push([cm.lat, cm.lng, 1.0]);
+        } else if(cm.rx > 10 && cm.rx < 12) {
+            heatRxY.push([cm.lat, cm.lng, 1.0]);
+        } else {
+            heatRxR.push([cm.lat, cm.lng, 1.0]);
+        }
+
+
+        if(cm.tx < 34) {
+            heatTxR.push([cm.lat, cm.lng, 1.0]);
+        } else if(cm.tx >= 34 && cm.tx < 40) {
+            heatTxY.push([cm.lat, cm.lng, 1.0]);
+        } else if(cm.tx >= 40 && cm.tx < 50) {
+            heatTxG.push([cm.lat, cm.lng, 1.0]);
+        } else if(cm.tx >= 50 && cm.tx < 54) {
+            heatTxY.push([cm.lat, cm.lng, 1.0]);
+        } else {
+            heatTxR.push([cm.lat, cm.lng, 1.0]);
+        }
+
+    }
+
+    map.fitBounds(bounds);
+    
+
+    var gradientOn = {0.0: '#00a65a', 1.0: '#00a65a'};
+    var gradientOff = {0.0: '#ff0000', 1.0: '#ff0000'};
+    
+    var online = L.heatLayer(heatOn, {blur: 10, radius: 10, maxZoom:10, gradient: gradientOn});
+    var offline = L.heatLayer(heatOff, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientOff});
+    var statusGroup = L.layerGroup([online,offline]).addTo(window.map);
+
+    var gradientR = {0.0: '#ff4d3f', 1.0: '#ff4d3f'};
+    var gradientY = {0.0: '#ffa732', 1.0: '#ffa732'};
+    var gradientG = {0.0: '#00a65a', 1.0: '#00a65a'};
+    
+    var rxR = L.heatLayer(heatRxR, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientR});
+    var rxY = L.heatLayer(heatRxY, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientY});
+    var rxG = L.heatLayer(heatRxG, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientG});
+    var rxGroup = L.layerGroup([rxR, rxY, rxG]);
+    
+    var txR = L.heatLayer(heatTxR, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientR});
+    var txY = L.heatLayer(heatTxY, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientY});
+    var txG = L.heatLayer(heatTxG, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientG});
+    var txGroup = L.layerGroup([txR, txY, txG]);
+
+
+    
+    var overlayMaps = {"{{ 'Status'|trans({}, 'StatsBundle') }}": statusGroup, "{{ 'RX Power'|trans({}, 'StatsBundle') }}": rxGroup, "{{ 'TX Power'|trans({}, 'StatsBundle') }}": txGroup};
+
+    L.control.layers(overlayMaps,{},{collapsed: false}).addTo(window.map);
+
+}
+
+</script>

+ 7 - 7
src/StatsBundle/Resources/views/Stats/Template/interface_cms_location.html.twig

@@ -27,14 +27,14 @@ function initializeMap() {
     
     window.map = new L.Map('map', { center: new L.LatLng({{location['lat']}}, {{location['lng']}}), zoom: {{location['zoom']}} });
 
-    var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
-    var osmAttrib = '&copy; OpenStreetMap';
-    var osm = L.tileLayer(osmUrl, { maxZoom: 18, attribution: osmAttrib });
+    var gstreet =  L.tileLayer('https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{
+                attribution: 'Google Inc.',
+                maxNativeZoom: 22,
+                maxZoom: 24,
+                subdomains:['mt0','mt1','mt2','mt3']
+    });
 
-    var controlLayers = L.control.layers({
-        'OSM': osm.addTo(window.map),
-        "Google": L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', {zIndex:1, attribution: '&copy; Google'})
-    }, {}, { position: 'topleft', collapsed: false });
+    var controlLayers = L.control.layers({"Street": gstreet.addTo(window.map)}, {}, { position: 'topleft', collapsed: false });
 
     
     bounds = [];

+ 4 - 1
src/StatsBundle/Resources/views/Stats/Template/load_svg.html.twig

@@ -1,4 +1,7 @@
-<svg id="loader" style="display:none; float:left" class="lds-polar" width="35px" height="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><g transform="translate(50 50)"><g transform="scale(0.8780218186353332)">
+{% if width is not defined %}
+{% set width = '35px' %}
+{% endif %}
+<svg id="loader" style="display:none; float:left" class="lds-polar" width="{{width}}" height="100%" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><g transform="translate(50 50)"><g transform="scale(0.8780218186353332)">
 <path d="M0 0L0 -40A40 40 0 0 1 34.64101615137754 -20.000000000000004" transform="rotate(0 0 0)" stroke="none" fill="#1d3f72"></path>
 <animateTransform attributeName="transform" type="scale" values="0.8780218186353332;0.884786130589434;0.5342315887395934;0.17453079196443083;0.7159374117286236;0.8780218186353332" keyTimes="0;0.2;0.4;0.6;0.8;1" dur="1.5s" repeatCount="indefinite"></animateTransform>
 </g><g transform="scale(0.02223813056279795)">

+ 146 - 0
src/StatsBundle/Resources/views/Stats/Template/olt_onus_location.html.twig

@@ -0,0 +1,146 @@
+{# {{dump(locations)}} #}
+
+<div style="padding: 10px">
+    <div style="text-align: center">
+    <span class="no_defined nas_data_origen">{{'Onus'|trans({}, 'StatsBundle') }}
+    &nbsp;&nbsp;({{locations|length}} {{ 'onus_with_lat_lng'|trans({}, 'StatsBundle') }})
+    </span>
+    </div>
+    <div id="div-map-loader">{% include '@Stats/Stats/Template/load_svg.html.twig' with {'width': '120px'} %}</div>
+    <div id="map" style="height:575px;display:hidden"></div>
+</div>
+
+
+
+<script>
+
+/* --------------------- PROGRAMA PRINCIPAL ------------------------- */
+
+var elements = {{locations|json_encode|raw}};
+var loc = {{location|json_encode|raw}};
+var initializedMap = false;
+
+/* --------------------- Carga Mapa -------------------------------- */
+
+function initializeMap() {
+
+    initializedMap = true;
+    $("#div-map-loader").hide();
+    $("#map").show();
+
+    //window.map = new L.Map('map', { center: new L.LatLng({{location['lat']}}, {{location['lng']}}), zoom: {{location['zoom']}} });
+    window.map = new L.Map('map', { center: new L.LatLng(loc.lat, loc.lng), zoom: loc.zoom });
+    
+    map.invalidateSize();
+
+    var gstreet =  L.tileLayer('https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{
+                attribution: 'Google Inc.',
+                maxNativeZoom: 22,
+                maxZoom: 24,
+                subdomains:['mt0','mt1','mt2','mt3']
+            });
+
+    var controlLayers = L.control.layers({
+        "Street": gstreet.addTo(window.map),
+        "Satellite": L.tileLayer('https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',{
+            attribution: 'Google Inc.',
+            maxNativeZoom: 21,
+            maxZoom: 21,
+            subdomains:['mt0','mt1','mt2','mt3']
+        }),
+        "Hybrid": L.tileLayer('https://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}',{
+            attribution: 'Google Inc.',
+            maxNativeZoom: 21,
+            maxZoom: 21,
+            subdomains:['mt0','mt1','mt2','mt3']
+        }),
+    }, {}, {position: 'topleft',collapsed: true});
+
+    controlLayers.addTo(window.map);
+    
+    bounds = [];
+    heatOn = [];
+    heatOff = [];
+    heatRxR = [];
+    heatRxY = [];
+    heatRxG = [];
+    heatTxR = [];
+    heatTxY = [];
+    heatTxG = [];
+    
+    for(e in elements) {
+        
+        var onu = elements[e];
+
+        if(onu.status) {
+            heatOn.push([onu.lat, onu.lng, 1.0]);
+        } else {
+            heatOff.push([onu.lat, onu.lng, 1.0]);
+        }
+
+        bounds.push([onu.lat, onu.lng]);
+
+        if(onu.rx <= -30) {
+            heatRxR.push([onu.lat, onu.lng, 1.0]);
+        } else if(onu.rx > -30 && onu.rx < -28) {
+            heatRxR.push([onu.lat, onu.lng, 1.0]);
+        } else if(onu.rx >= -28 && onu.rx < -26) {
+            heatRxY.push([onu.lat, onu.lng, 1.0]);
+        } else if(onu.rx >= -26 && onu.rx < 20) {
+            heatRxG.push([onu.lat, onu.lng, 1.0]);
+        } else if(onu.rx >= -20 && onu.rx < -15) {
+            heatRxY.push([onu.lat, onu.lng, 1.0]);
+        } else if(onu.rx >= -15 && onu.rx < -13) {
+            heatRxR.push([onu.lat, onu.lng, 1.0]);
+        } else {
+            heatRxR.push([onu.lat, onu.lng, 1.0]);
+        }
+
+        if(onu.tx < 1) {
+            heatTxR.push([onu.lat, onu.lng, 1.0]);
+        } else if(onu.tx >= 1 && onu.tx < 1.5) {
+            heatTxY.push([onu.lat, onu.lng, 1.0]);
+        } else if(onu.tx >= 1.5 && onu.tx < 3.5) {
+            heatTxG.push([onu.lat, onu.lng, 1.0]);
+        } else if(onu.tx >= 3.5 && onu.tx < 4.5) {
+            heatTxY.push([onu.lat, onu.lng, 1.0]);
+        } else {
+            heatTxR.push([onu.lat, onu.lng, 1.0]);
+        }
+
+    }
+
+    map.fitBounds(bounds);
+    
+
+    var gradientOn = {0.0: '#00a65a', 1.0: '#00a65a'};
+    var gradientOff = {0.0: '#ff0000', 1.0: '#ff0000'};
+    var online = L.heatLayer(heatOn, {blur: 10, radius: 10, maxZoom:10, gradient: gradientOn});
+    var offline = L.heatLayer(heatOff, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientOff});
+    var statusGroup = L.layerGroup([online,offline]).addTo(window.map);
+
+    var gradientR = {0.0: '#ff4d3f', 1.0: '#ff4d3f'};
+    var gradientY = {0.0: '#ffa732', 1.0: '#ffa732'};
+    var gradientG = {0.0: '#00a65a', 1.0: '#00a65a'};
+    
+    var rxR = L.heatLayer(heatRxR, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientR});
+    var rxY = L.heatLayer(heatRxY, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientY});
+    var rxG = L.heatLayer(heatRxG, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientG});
+    var rxGroup = L.layerGroup([rxR, rxY, rxG]);
+    
+    var txR = L.heatLayer(heatTxR, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientR});
+    var txY = L.heatLayer(heatTxY, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientY});
+    var txG = L.heatLayer(heatTxG, {blur: 10, radius: 10, maxZoom: 10, gradient: gradientG});
+    var txGroup = L.layerGroup([txR, txY, txG]);
+    
+    var overlayMaps = {"{{ 'Status'|trans({}, 'StatsBundle') }}": statusGroup, "{{ 'RX Power'|trans({}, 'StatsBundle') }}": rxGroup, "{{ 'TX Power'|trans({}, 'StatsBundle') }}": txGroup};
+
+    L.control.layers(overlayMaps,{},{collapsed: false}).addTo(window.map);
+
+    console.log(heatRxR);
+    console.log(heatRxY);
+    console.log(heatRxG);
+        
+}
+
+</script>

+ 7 - 5
src/StatsBundle/Resources/views/Stats/Template/onu_location.html.twig

@@ -22,13 +22,15 @@ function initializeMap() {
 
     window.map = new L.Map('map', { center: new L.LatLng({{location['lat']}}, {{location['lng']}}), zoom: {{location['zoom']}} });
 
-    var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
-    var osmAttrib = '&copy; OpenStreetMap';
-    var osm = L.tileLayer(osmUrl, { maxZoom: 18, attribution: osmAttrib });
+    var gstreet =  L.tileLayer('https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{
+                attribution: 'Google Inc.',
+                maxNativeZoom: 22,
+                maxZoom: 24,
+                subdomains:['mt0','mt1','mt2','mt3']
+    });
 
     var controlLayers = L.control.layers({
-        'OSM': osm.addTo(window.map),
-        "Google": L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', {zIndex:1, attribution: '&copy; Google'})
+        "Street": gstreet.addTo(window.map),
     }, {}, { position: 'topleft', collapsed: false });
 
     {% if location['marker'] %}

+ 7 - 5
src/StatsBundle/Resources/views/Stats/Template/ponport_onus_location.html.twig

@@ -20,13 +20,15 @@ function initializeMap() {
     
     window.map = new L.Map('map', { center: new L.LatLng({{location['lat']}}, {{location['lng']}}), zoom: {{location['zoom']}} });
 
-    var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
-    var osmAttrib = '&copy; OpenStreetMap';
-    var osm = L.tileLayer(osmUrl, { maxZoom: 18, attribution: osmAttrib });
+    var gstreet =  L.tileLayer('https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{
+                attribution: 'Google Inc.',
+                maxNativeZoom: 22,
+                maxZoom: 24,
+                subdomains:['mt0','mt1','mt2','mt3']
+    });
 
     var controlLayers = L.control.layers({
-        'OSM': osm.addTo(window.map),
-        "Google": L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', {zIndex:1, attribution: '&copy; Google'})
+        "Street": gstreet.addTo(window.map),
     }, {}, { position: 'topleft', collapsed: false });
 
     

+ 21 - 1
src/StatsBundle/Resources/views/Stats/show_cmts.html.twig

@@ -29,6 +29,8 @@ table.dataTable thead tr th, table.dataTable tbody tr td {
 <script src="{{ asset('bundles/stats/highcharts/code/highcharts-more.js') }}" type="text/javascript" charset="utf-8"></script>
 <script src="{{ asset('bundles/stats/highcharts/code/modules/data.js') }}" type="text/javascript" charset="utf-8"></script>
 <script src="{{ asset('bundles/stats/highcharts/code/modules/exporting.js') }}" type="text/javascript" charset="utf-8"></script>
+<script src="{{ asset('bundles/stats/js/simple-heat.js') }}" type="text/javascript" charset="utf-8"></script>
+<script src="{{ asset('bundles/stats/js/leaflet-heatmap-src.js') }}" type="text/javascript" charset="utf-8"></script>
 
 {% endblock %}
 
@@ -85,6 +87,9 @@ table.dataTable thead tr th, table.dataTable tbody tr td {
                         <li>
                             <a class="tab-link-star" href="#tab_6" data-toggle="tab">{{ 'STAR'|trans({}, 'StatsBundle') }}</a>
                         </li>
+                        <li>
+                            <a class="tab-link" id="tab_map" href="#tab_7" data-toggle="tab">{{ 'Map'|trans({}, 'StatsBundle') }}</a>
+                        </li>
                     </ul>
                     <div class="tab-content clearfix">
                         {# TAB Description #}
@@ -247,6 +252,14 @@ table.dataTable thead tr th, table.dataTable tbody tr td {
                             </div>
                         </div>
 
+                        {# TAB MAP #}
+                        <div class="tab-pane" id="tab_7" style="width: 100%">
+                            {# <div class="col-sm-12" style="width: 100%"> #}
+                            <div class="col-sm-12" style="height:600px">
+                                {% include '@Stats/Stats/Template/cmts_cms_location.html.twig' %}
+                            </div>
+                        </div>
+
                     </div>
                 </div>
             </div>
@@ -269,7 +282,14 @@ $('.tab-link-star').click( function(e) {
 
 $('#flap_list').click( function(e) {
     loadFlapList();
-}); 
+});
+
+$('#tab_map').click( function(e) {
+    if(! initializedMap ) {
+        setTimeout(function(){ initializeMap() }, 2000);
+    }
+});
+
 
 var from = {{from}};
 var to = {{to}};

+ 19 - 1
src/StatsBundle/Resources/views/Stats/show_olt.html.twig

@@ -22,13 +22,15 @@ table.dataTable thead tr th, table.dataTable tbody tr td {
 {% block javascripts %}
 {{ parent() }}
 
-{# {% include 'LeafletBundle:Leaflet:resources_only_leaflet.html.twig' %} #}
+{% include 'LeafletBundle:Leaflet:resources_only_leaflet.html.twig' %}
 <script src="{{ asset('bundles/stats/highcharts/code/highcharts.js') }}" type="text/javascript" charset="utf-8"></script>
 <script src="{{ asset('bundles/stats/highcharts/highchartsConfig.js') }}" type="text/javascript" charset="utf-8"></script>
 <script src="{{ asset('bundles/stats/datatables/jquery.dataTables.min.js') }}"></script>
 <script src="{{ asset('bundles/stats/highcharts/code/highcharts-more.js') }}" type="text/javascript" charset="utf-8"></script>
 <script src="{{ asset('bundles/stats/highcharts/code/modules/data.js') }}" type="text/javascript" charset="utf-8"></script>
 <script src="{{ asset('bundles/stats/highcharts/code/modules/exporting.js') }}" type="text/javascript" charset="utf-8"></script>
+<script src="{{ asset('bundles/stats/js/simple-heat.js') }}" type="text/javascript" charset="utf-8"></script>
+<script src="{{ asset('bundles/stats/js/leaflet-heatmap-src.js') }}" type="text/javascript" charset="utf-8"></script>
 
 {% endblock %}
 
@@ -72,6 +74,9 @@ table.dataTable thead tr th, table.dataTable tbody tr td {
                         <li>
                             <a class="tab-link-star" href="#tab_2" data-toggle="tab">{{ 'STAR'|trans({}, 'StatsBundle') }}</a>
                         </li>
+                        <li>
+                            <a class="tab-link" id="tab_map" href="#tab_3" data-toggle="tab">{{ 'Map'|trans({}, 'StatsBundle') }}</a>
+                        </li>
                     </ul>
                     <div class="tab-content clearfix">
                         
@@ -182,6 +187,13 @@ table.dataTable thead tr th, table.dataTable tbody tr td {
                             </div>
                         </div>
 
+                        {# TAB MAP #}
+                        <div class="tab-pane" id="tab_3" style="width: 100%">
+                            <div class="col-sm-12" style="height:600px">
+                                {% include '@Stats/Stats/Template/olt_onus_location.html.twig' %}
+                            </div>
+                        </div>
+
                     </div>
                 </div>
             </div>
@@ -202,6 +214,12 @@ $('.tab-link-star').click( function(e) {
     reloadStarList();
 });
 
+$('#tab_map').click( function(e) {
+    if(! initializedMap ) {
+        setTimeout(function(){ initializeMap() }, 2000);
+    }
+});
+
 var from = {{from}};
 var to = {{to}};
 var markerEnable = false;