Source: directives/mapa/mapa.js

/**
 * @ngdoc directives
 * @name Mapa
 * @module s4c.directives.mapa.Mapa
 *
 * @description
 * `Mapa` módulo de mapas do sistema, permite iterar sobre diversos
 * tipos de mapa, open source ou não, desde que haja compatibilidade com o
 * leaflet, que é a base.
 *
 * @example   <s4c-mapa></s4c-mapa>
 * 
 * 
 */
(function () {
    'use strict';

    s4cMapa.$inject = [
        'MapaStateService',
        'leafletData',
        'PlanejamentoDesenhoService',
        'CamadasService',
        'RotasService',
        'RotasUnificadasService',
        'LegendaIncidenteFilter',
        'IncidenteFilter',
        'RotasManager',
        'RotasUnificadasManager',
        'MainState',
        'Geocode',
        'localize',
        'RotasUnificadasFilter',
        '$state',
        '$window',
        'BlackList',
        'MainState',
        '$timeout'
    ];

    function s4cMapa() {

        return {
            restrict: 'E',
            templateUrl: 'app/directives/mapa/mapa.html',
            replace: true,
            controller: MapaCtrl,
            link: function ($scope, $elem, $attrs) {
                $scope.res = $scope.$root.res;
                $scope.cameraList = $elem.find('#cameraList');
                $scope.poiAtivo = {};
            }
        };
    }

    MapaCtrl.$inject = [
        '$scope',
        '$rootScope',
        'API_ENDPOINT',
        '$mdDialog',
        '$timeout',
        'FileUploader',
        'leafletData',
        '$mdBottomSheet',
        '$sce',
        '$q',
        'RotasService',
        'RotasUnificadasService',
        'PlanejamentoDesenhoService',
        'toasty',
        'Camera',
        'MainState',
        'IncidenteEnterManager',
        'ZonaDeObservacaoManager',
        'RotasManager',
        'RotasUnificadasManager',
        'CamerasManager',
        'CamadasService',
        'PlanejamentoManager',
        'DetalhamentoManager',
        'PermissoesService',
        'localize',
        'Geocode',
        'RastreamentoManager',
        'PontoMovelManager',
        'MapaStateService',
        'MapaManager',
        'AuthService',
        'MapaService',
        'ParametrosS4C',
        'PlanejamentoService',
        'IncidenteFilter',
        'LegendaIncidenteFilter',
        'BlackList',
        '$interval',
        'VideoStreamService'
    ];
    function MapaCtrl($scope,
        $rootScope,
        API_ENDPOINT,
        $mdDialog,
        $timeout,
        FileUploader,
        leafletData,
        $mdBottomSheet,
        $sce,
        $q,
        RotasService,
        RotasUnificadasService,
        PlanejamentoDesenhoService,
        toasty,
        Camera,
        MainState,
        IncidenteEnterManager,
        ZonaDeObservacaoManager,
        RotasManager,
        RotasUnificadasManager,
        CamerasManager,
        CamadasService,
        PlanejamentoManager,
        DetalhamentoManager,
        PermissoesService,
        localize,
        Geocode,
        RastreamentoManager,
        PontoMovelManager,
        MapaStateService,
        MapaManager,
        AuthService,
        MapaService,
        ParametrosS4C,
        PlanejamentoService,
        IncidenteFilter,
        LegendaIncidenteFilter, BlackList, $interval, VideoStreamService) {

        $scope.res = $scope.$root.res;
        var mapaPrincipal = Math.random() * 10;

        if ($rootScope.ParametrosS4C.hasOwnProperty('mapaZoomMax') && $rootScope.ParametrosS4C.mapaZoomMax != null) {
            _obterMapa()
                .then(function (map) {
                    MapaStateService.mapa = map;
                    MapaStateService.mapa.options.maxZoom = $rootScope.ParametrosS4C.mapaZoomMax;
                });
        }

        PermissoesService.getPermissoes().then(function (perms) {
            $scope.permissoesVisualizacao = perms.permissoesVisualizacao;
            $scope.permissoesCriacao = perms.permissoesCriacao;
            $scope.permissoesEdicao = perms.permissoesEdicao;
            $scope.permissoesRemocao = perms.permissoesRemocao;
            $scope.permissoesExtras = perms.permissoesExtras;

            if (!_.includes(perms.permissoesCriacao, true)) {
                $scope.nenhumaPermissaoCriacao = true;
            }
        });

        var LeafIcon = L.Icon.extend({
            options: {
                iconSize: [48, 48],
                html: 'texto'
            }
        });

        BlackList.obter().then(function (blacklist) {
            $scope.blackList = blacklist;
        });

        $scope.isToShow = function (label) {
            return MainState.isToShow(label);
        }

        var LeafIcon = function (index) {
            return L.divIcon({
                iconSize: new L.Point(48, 48),
                html: '<div class="letter">' + converte(index) + '</div>',
                className: 'my-div-icon'
            })
        };


        function converte(index) {
            return ' ' + String.fromCharCode(65 + index);
        }

        $scope.$on('$destroy', function () {
            CamadasService.limparCamadas();
            LegendaIncidenteFilter.limparFiltros();
            IncidenteFilter.limparFiltros();
        });
        /**
         * Leitura de arquivon json através de requisição rest
         * 
         * @method readTextFile
         * 
         * @param file {File}
         * @param callback {Function}
         * 
         */ 
        function readTextFile(file, callback) {
            var rawFile = new XMLHttpRequest();
            rawFile.overrideMimeType("application/json");
            rawFile.open("GET", file, true);
            rawFile.onreadystatechange = function () {
                if (rawFile.readyState === 4 && (rawFile.status == "200" || rawFile.status == 0)) {
                    callback(rawFile.responseText);
                }
            }
            rawFile.send(null);
        }

        //usage:
        readTextFile("/s4c_dir/mapbox.json", function (text) {
            var data = JSON.parse(text);
            $scope.mapbox_access_token_api = data.mapbox_access_token_api;

            $timeout(function () {
                _instaciarRotaUnificada();
            }, 1500);

        });


        /**
         * Configura o módulo de rota pra ser usado no mapa
         * 
         * @method _instaciarRotaUnificada
         * 
         * 
         */           
        function _instaciarRotaUnificada() {
            // rota unificada
            _obterMapa()
                .then(function (map) {
                    MapaStateService.mapa = map;

                    $scope.router_unificada = L.Routing.control({
                        plan: L.Routing.plan([], {
                            createMarker: function (i, obj) {
                                if (RotasUnificadasManager.rotaAtiva.rotaUnificadaId != null &&
                                    ((RotasUnificadasManager.rotaAtiva.pontos[0].latLng.lat == obj.latLng.lat && RotasUnificadasManager.rotaAtiva.pontos[0].latLng.lng == obj.latLng.lng)
                                        || (RotasUnificadasManager.rotaAtiva.pontos[RotasUnificadasManager.rotaAtiva.pontos.length - 1].latLng.lat == obj.latLng.lat && RotasUnificadasManager.rotaAtiva.pontos[RotasUnificadasManager.rotaAtiva.pontos.length - 1].latLng.lng == obj.latLng.lng))) {
                                    var marker = new L.marker(obj.latLng, {
                                        draggable: false,
                                        icon: LeafIcon(i)
                                    });

                                    return marker;
                                } else {
                                    var marker = new L.marker(obj.latLng, {
                                        draggable: true,
                                        icon: LeafIcon(i)
                                    });
                                    return marker;
                                }
                            }
                        }),

                        router: L.Routing.mapbox($scope.mapbox_access_token_api),
                        geocoder: new L.Control.Geocoder.Google()

                    }).addTo(map).addEventListener('routesfound', function (e) {
                        console.log('Adiciona novas rotas'); // HERE
                        RotasUnificadasManager.rotaAtiva.pontos = $scope.router_unificada.getWaypoints();
                        var waypoints = [];
                        _.reduce($scope.router_unificada.getWaypoints(), function (a, sum) {
                            waypoints.push({
                                de: a.latLng,
                                para: sum.latLng
                            });
                            return sum;
                        });
                        RotasUnificadasManager.rotaAtiva.rota = e.routes[0].coordinates;

                        RotasUnificadasService.atualizarPontos(RotasUnificadasManager.rotaAtiva.pontos);

                        if (RotasUnificadasManager.rotaAtiva.id != null) {
                            RotasUnificadasService.calcularTempoRotaCriada({
                                trechos: waypoints
                            }, RotasUnificadasManager.rotaAtiva.id)
                                .then(function (tempo) {

                                    var value = ((100 * (tempo.currentTime / tempo.regularTime)) - 100);

                                    if (value <= 10) {
                                        RotasUnificadasManager.rotaAtiva.cor = '#8AC249';
                                    } else if (value >= 10 && value <= 50) {
                                        RotasUnificadasManager.rotaAtiva.cor = '#FFFF00';
                                    } else {
                                        RotasUnificadasManager.rotaAtiva.cor = '#F34235';
                                    }

                                    RotasUnificadasManager.rotaAtiva.tempo_atual = tempo.currentTime;
                                    RotasUnificadasManager.rotaAtiva.tempo_regular = tempo.regularTime;
                                    RotasUnificadasManager.atualizarTempo(RotasUnificadasManager.rotaAtiva);
                                });

                        }
                        else {
                            RotasUnificadasService.calcularTempo({
                                trechos: waypoints
                            })
                                .then(function (tempo) {

                                    var value = ((100 * (tempo.currentTime / tempo.regularTime)) - 100);

                                    if (value <= 10) {
                                        RotasUnificadasManager.rotaAtiva.cor = '#8AC249';
                                    } else if (value >= 10 && value <= 50) {
                                        RotasUnificadasManager.rotaAtiva.cor = '#FFFF00';
                                    } else {
                                        RotasUnificadasManager.rotaAtiva.cor = '#F34235';
                                    }

                                    RotasUnificadasManager.rotaAtiva.tempo_atual = tempo.currentTime;
                                    RotasUnificadasManager.rotaAtiva.tempo_regular = tempo.regularTime;
                                });
                        }

                        var startIcon = L.icon({
                            iconUrl: '/assets/images/Rotas/s4c_ic_route_beginning_color1.svg',
                            iconSize: [48, 48]
                        });

                        var endIcon = L.icon({
                            iconUrl: '/assets/images/Rotas/s4c_ic_route_beginning_final.svg',
                            iconSize: [36, 36]
                        });

                        $scope.router_unificada._plan._markers[0].setIcon(startIcon);
                        $scope.router_unificada._plan._markers[$scope.router_unificada._plan._markers.length - 1].setIcon(endIcon);

                    });
                });
            // rota unificada
        }

        var labels = {
            'camera': function (feature) {
                return feature.properties.nome;
            },
            'incidente': function (feature) {
                return feature.properties.nome;
            },
            'base_conhecimento': function (feature) {
                return feature.properties.nome;
            },
            'planejamento': function (feature) {
                return feature.properties.nome;
            },
            'zona_observacao': function (feature) {
                return feature.properties.nome;
            },
            'poi': function (feature) {
                return feature.properties.nome;
            },
            'viatura': function (feature) {

                return feature.properties.nome ||
                    (feature.properties.agencia + ' - ' + feature.properties.chave_estrangeira);
            },
            'terminal': function (feature) {
                return feature.properties.nome;
            },
        };

        /**
         * Instancia um marker no mapa 
         * 
         * @method PontoInfo
         * 
         * @param obj {Object}
         * @param mapa {Object}
         * @param lat {String}
         * @param lng {String}
         * 
         */         
        function PontoInfo(obj, mapa, lat, lng) {
            this.marker = obj;
            this.mapa = mapa;
            this.lat = lat;
            this.lng = lng;
            this.oldLng = lng;
            this.oldLat = lat;

            this.destroy = function () {
                removerIcon(this);
            }

            this.draggable = function () {
                this.marker.dragging.enable();
            }

            this.undraggable = function () {
                this.marker.dragging.disable();
            }

            this.updateOldPosition = function () {
                this.oldLng = this.lng;
                this.oldLat = this.lat;
            }
        }

        /**
         * Configuração do click nos objetos do mapa 
         * 
         * @method click
         * 
         * @param id {Object}
         * 
         */                
        var click = {
            'base_conhecimento': function (id) {

                var BaseConhecimentoManager = MainState.getManager('BaseConhecimentoManager');

                return function (e) {
                    var pontoInfo = new PontoInfo(e.target, 2,
                        e.latlng.lat,
                        e.latlng.lng);
                    BaseConhecimentoManager.teste(id, pontoInfo);
                }
            },
            'planejamento': function (id) {

                var PlanejamentoManager = MainState.getManager('PlanejamentoManager');

                return function (e) {
                    PlanejamentoService.getPlanejamento(id)
                        .then(function (data) {
                            PlanejamentoManager.abrir(
                                data,
                                new PontoInfo(e.target, 2, e.target.getLatLng().lat, e.target.getLatLng().lng));
                        });
                }
            },
            'incidente': function (id) {

                var DetalhamentoManager = MainState.getManager('DetalhamentoManager');
                var SubDetalhamentoManager = MainState.getManager('SubDetalhamentoManager');

                return function (e) {
                    clearInterval($scope.poiAtivo);

                    if (DetalhamentoManager.ativo) {
                        var incidentesProximos = DetalhamentoManager.incidentesProximos;
                        var index = _.findIndex(incidentesProximos, {
                            id: id
                        });

                        // Poi clicado é um subitem
                        if (index > -1) {
                            limparSubItem();
                            var ativo = DetalhamentoManager.data;
                            var markerLatLng = e.target.getLatLng();
                            var geojson = {
                                coordinates: [
                                    markerLatLng.lng,
                                    markerLatLng.lat
                                ]
                            };
                            DetalhamentoManager.voarParaSubItem(ativo, {
                                geojson: JSON.stringify(geojson)
                            });
                            SubDetalhamentoManager.abrStreamedianirIncidente(id, e.latlng, e.target);
                        } else {
                            DetalhamentoManager.resetarDetalhamento();
                            SubDetalhamentoManager.resetarSubDetalhamento();
                            DetalhamentoManager.abrirIncidente(id, e.latlng, e.target);
                            MapaService.piscarVermelho(e.latlng);
                        }
                    } else {
                        // Não existe nenhum poi clicado no momento, abrindo detalhamento:
                        piscarVermelho(e.latlng);
                        DetalhamentoManager.abrirIncidente(id, e.latlng, e.target);
                    }
                }
            },
            'zona_observacao': function (id) {
                var DetalhamentoManager = MainState.getManager('DetalhamentoManager');
                return function () {
                    DetalhamentoManager.abrirZonaDeObservacao(id);
                }
            },
            'polygon': function (id, latlng) {

                var DetalhamentoManager = MainState.getManager('DetalhamentoManager');
                var SubDetalhamentoManager = MainState.getManager('SubDetalhamentoManager');

                return function (e) {
                    if (DetalhamentoManager.ativo) {

                        var poisProximos = DetalhamentoManager.poisProximos;
                        var index = _.findIndex(poisProximos, {
                            id: id
                        });

                        DetalhamentoManager.resetarDetalhamento();
                        SubDetalhamentoManager.resetarSubDetalhamento();
                        DetalhamentoManager.abrirPoi(id, latlng);
                    } else {
                        // Não existe nenhum poi clicado no momento, abrindo detalhamento:
                        DetalhamentoManager.abrirPoi(id, latlng);
                    }
                }
            },
            'poi': function (id) {

                return MapaService.clickPoi(id);
            },
            'terminal': function (id) {
                var TerminalManager = MainState.getManager('TerminalManager');

                return function (e) {
                    piscarAzul(e.latlng);

                    TerminalService
                        .pegarTerminal(id)
                        .then(function (data) {
                            if (data.status === 'NULL') {
                                data.status = undefined;
                            }

                            data.Tarefas = [];
                            TerminalManager.abrir(data);

                            TerminalService
                                .pegarTarefas(data.chave_estrangeira)
                                .then(function (tarefas) {
                                    data.Tarefas = tarefas;
                                });
                        });
                }
            },
            'camera': function (id) {
                return function () {
                    Camera.obterPorId(id).then(function (camera) {
                        var id = Math.random().toString(36).substring(2, 15);
                        vModal('id' + id, camera.nome,
                            '<video id="' + id + '" controls autoplay style="width:100%; height:100%;"></video>', function () {
                                VideoStreamService.close(id);
                            });
                        VideoStreamService.open(id, camera.url);
                    });
                }
            },
            'viatura': function (viatura) {
                var DetalhamentoManager = MainState.getManager('DetalhamentoManager');
                var SubDetalhamentoManager = MainState.getManager('SubDetalhamentoManager');

                return function (e) {
                    if (DetalhamentoManager.ativo) {

                        DetalhamentoManager.resetarDetalhamento();
                        SubDetalhamentoManager.resetarSubDetalhamento();
                        DetalhamentoManager.abrirViatura(viatura);
                        piscarAzul(e.latlng);
                    } else {
                        // Não existe nenhum poi clicado no momento, abrindo detalhamento:
                        DetalhamentoManager.abrirViatura(viatura);
                        piscarAzul(e.latlng);
                    }
                }
            }
        };

        $scope.uploader = new FileUploader();
        var options = {
            lng: function (d) {
                return d[0];
            },
            lat: function (d) {
                return d[1];
            },
            duration: 800,
            efficient: {
                enabled: false,
                fps: 18
            }
        };

        /**
         * Configuração das câmeras que podem ser abertas no Multivis 
         * 
         * @method _openCameraMultivis
         * 
         * @param urlCamera {Object}
         * 
         */            
        function _openCameraMultivis(urlCamera) {
            var parameterMultivis = {
                //"app": "app_0",
                "application": "ctx_Camera",
                "parameters": {
                    "camera1": urlCamera
                },
                "functionOnApp": {
                    "func": "loadCamera1",
                    "parameters": {
                        "clientInput": urlCamera
                    }
                }
            };
            if ($rootScope.ParametrosS4C.multivisConfig
                && $rootScope.ParametrosS4C.multivisConfig.key
                && $rootScope.ParametrosS4C.multivisConfig.hostname) {
                $.ajax({
                    url: $rootScope.ParametrosS4C.multivisConfig.hostname + "/createApplication",
                    method: "POST",
                    dataType: "json",
                    headers: {
                        "integration": $rootScope.ParametrosS4C.multivisConfig.key
                    },
                    data: JSON.stringify(parameterMultivis),
                    success: function (data) {
                        console.log(data)
                    },
                    error: function (data) {
                        if (data.status != 200) {
                            $mdDialog
                                .show($mdDialog.alert()
                                    .title(localize.res('COMUM_ERRO'))
                                    .content(localize.res('MULTIVIS_OFFLINE', $rootScope.ParametrosS4C.multivisConfig.hostname))
                                    .ok(localize.res('COMUM_OK')));
                        }
                    }
                });
            } else {
                $mdDialog
                    .show($mdDialog.alert()
                        .title(localize.res('COMUM_ERRO'))
                        .content('Servidor Multivis Não configurado')//TODO internacionalizar
                        .ok(localize.res('COMUM_OK')));
            }
        }

        // PING LAYER:
        $scope.pingLayer = L.pingLayer(options);
        $scope.pingLayer.radiusScale().range([0, 50]);
        $scope.pingLayer.opacityScale().range([1, 0]);

        $scope.lines = new jsGraphics("lineCanvas");
        $scope.circle = new jsGraphics("lineCanvas");

        var IncidenteEnterManager = MainState.getManager('IncidenteEnterManager');
        _obterMapa()
            .then(function (map) {
                $scope.pingLayer.addTo(map);
            });

        /**
         * Configuração dos trechos das rotas que são desenhados no mapa
         * 
         * @method desenharPoligono
         * 
         * @param rota {Object}
         * 
         */              
        function desenharPoligono(rota) {
            var deferred = $q.defer();

            var linestring = turf.linestring(rota.shape.coordinates);
            var pontos = _.chain(rota.RotaTrechos)
                .map(function (ponto) {
                    return {
                        de: turf.point(_.clone(ponto.de.coordinates).reverse()),
                        para: turf.point(_.clone(ponto.para.coordinates).reverse())
                    };
                })
                .value();

            var inicio = _.head(pontos).de;
            var fim = _.last(pontos).para;

            inicio.properties.icon = '/data-s4c/Camadas/Rotas/s4c_ic_starting_point.png';
            fim.properties.icon = '/assets/images/Rotas/s4c_ic_route_beginning_final.svg';
            /*
                        var ultimoPonto = _.last(rota.RotaTrechos).para;
                        pontos.push(turf.point(_.clone(ultimoPonto.coordinates).reverse()));
            */
            var geojson = turf.featurecollection(_.flatten([inicio, linestring, fim]));
            var geoJSON = L.geoJson(geojson, {
                id: rota.id,
                rota: true,
                rotaId: rota.id,
                pointToLayer: function (feature, latlng) {

                    if (feature.geometry.type === 'Point') {

                        var smallIcon = L.icon({
                            iconSize: [27, 27],
                            iconAnchor: [13, 27],
                            popupAnchor: [1, -24],
                            iconUrl: feature.properties.icon
                        });

                        return L.marker(latlng, { icon: smallIcon });
                    } else {
                        return new L.Polygon(latlng, {
                            color: rota.cor,
                            opacity: 0.8,
                            weight: 9
                        });
                    }

                },
                onEachFeature: function (feature, layer) {

                    layer.on('click', function () {
                        RotasService.pegarRota(rota.id)
                            .then(function (rota) {
                                RotasManager.editar(rota);
                            });
                    });
                },
            });

            _obterMapa()
                .then(function (map) {

                    map.eachLayer(function (layer) {
                        if (layer.options && layer.options.rota) {
                            map.removeLayer(layer);
                        }
                    });

                    map.addLayer(geoJSON);
                    map.fitBounds(geoJSON.getBounds());

                    deferred.resolve(geoJSON);
                });

            return deferred.promise;
        }

        /**
         * Remove camada do mapa
         * 
         * @method removeLayer
         * 
         * @param aggregate {Object}
         * 
         */            
        function removeLayer(aggregate) {
            _obterMapa()
                .then(function (map) {

                    map.removeLayer(aggregate);

                });
        }

        /**
         * Configuração dos trechos das rotas que são desenhados no mapa
         * 
         * @method desenharPoligonoRotaUnficada
         * 
         * @param rota {Object}
         * 
         */        
        function desenharPoligonoRotaUnficada(rota) {
            var deferred = $q.defer();

            var linestring = turf.linestring(rota.shape.coordinates);
            var pontos = _.chain(rota.RotaTrechos)
                .map(function (ponto) {
                    return {
                        de: turf.point(_.clone(ponto.de.coordinates).reverse()),
                        para: turf.point(_.clone(ponto.para.coordinates).reverse())
                    };
                })
                .value();

            var inicio = _.head(pontos).de;
            var fim = _.last(pontos).para;

            inicio.properties.icon = '/data-s4c/Camadas/Rotas/s4c_ic_starting_point.png';
            fim.properties.icon = '/assets/images/Rotas/s4c_ic_route_beginning_final.svg';
            /*
                        var ultimoPonto = _.last(rota.RotaTrechos).para;
                        pontos.push(turf.point(_.clone(ultimoPonto.coordinates).reverse()));
            */
            var geojson = turf.featurecollection(_.flatten([inicio, linestring, fim]));
            var geoJSON = L.geoJson(geojson, {
                id: rota.id,
                rota: true,
                rotaId: rota.id,
                pointToLayer: function (feature, latlng) {

                    if (feature.geometry.type === 'Point') {

                        var smallIcon = L.icon({
                            iconSize: [27, 27],
                            iconAnchor: [13, 27],
                            popupAnchor: [1, -24],
                            iconUrl: feature.properties.icon
                        });

                        return L.marker(latlng, { icon: smallIcon });
                    } else {
                        return new L.Polygon(latlng, {
                            color: rota.cor,
                            opacity: 0.8,
                            weight: 9
                        });
                    }

                },
                onEachFeature: function (feature, layer) {

                    layer.on('click', function () {
                        RotasUnificadasService.pegarRota(rota.id)
                            .then(function (rota) {
                                RotasUnificadasManager.editar(rota);
                            });
                    });
                },
            });


            _obterMapa()
                .then(function (map) {

                    map.eachLayer(function (layer) {
                        if (layer.options && layer.options.rota) {
                            map.removeLayer(layer);
                        }
                    });

                    map.addLayer(geoJSON);
                    map.fitBounds(geoJSON.getBounds());

                    deferred.resolve(geoJSON);
                });

            return deferred.promise;
        }

        /**
        * Ativa uma camada de um planejamento
        */
        /**
         * Ativa uma camada de um planejamento
         * 
         * @method ativarCamada
         * 
         * @param obj {Object}
         * @param cb {Object}
         * 
         */       
        function ativarCamada(obj, cb) {
            _obterMapa()
                .then(function (map) {

                    var circles = _.filter(obj.geoJSON.features, function (feature) {
                        return feature.properties.type === 'Circle';
                    });

                    var polygons = _.filter(obj.geoJSON.features, function (feature) {
                        return feature.properties.type !== 'Circle';
                    });

                    var polygonLayers = L.geoJson(polygons, {
                        pointToLayer: function (feature, latlng) {

                            var Icon = L.divIcon({
                                html: feature.properties.icon.html,
                                iconSize: [30, 70]
                            });

                            return L.marker(latlng, {
                                icon: Icon
                            });

                        },
                        onEachFeature: function (feature, layer) {
                            layer.on('mouseover mousemove', function (e) {
                                if (feature.properties.texto) {
                                    var hover_bubble = new L.Rrose({
                                        offset: new L.Point(0, -10),
                                        closeButton: false,
                                        autoPan: false
                                    })
                                        .setContent(feature.properties.texto)
                                        .setLatLng(e.latlng)
                                        .openOn(map);
                                }
                            });
                            layer.on('mouseout', function (e) {
                                map.closePopup()
                            });
                        },
                        style: function (feature) {
                            return feature.style;
                        }
                    });

                    var circleLayers = _.map(circles, function (circle) {
                        return L.circle([circle.geometry.coordinates[1], circle.geometry.coordinates[0]], circle.properties.radius, circle.style);
                    });

                    var layerGroup = L.layerGroup(circleLayers);

                    layerGroup.addLayer(polygonLayers);
                    layerGroup.addTo(map);

                    if (obj.pontosDaRota) {

                        obj.pontosDaRota.router = new L.Routing.control({
                            plan: L.Routing.plan([], {
                                createMarker: function (i, wp) {

                                    var icon = new L.Icon.Label.Default({
                                        labelText: String.fromCharCode(65 + i)
                                    });
                                    var marker = L.marker(wp.latLng, {
                                        draggable: false,
                                        icon: icon
                                    });

                                    return marker;
                                },
                                draggableWaypoints: false,
                                addWaypoints: false
                            }),
                            autoRoute: false,
                            fitSelectedRoutes: false,
                            geocoder: new L.Control.Geocoder.Google()

                        }).addTo(map)

                        obj.pontosDaRota.router.setWaypoints(_.map(obj.pontosDaRota, function (pontoDaRota) {
                            return {
                                name: pontoDaRota.endereco,
                                latLng: {
                                    lat: pontoDaRota.latitude,
                                    lng: pontoDaRota.longitude
                                }
                            }
                        }))
                    }

                    cb(layerGroup);
                });

        }

        /**
         * Desativa uma camada de um planejamento
         */
        /**
         * Desativa uma camada de um planejamento
         * 
         * @method desativarCamada
         * 
         * @param obj {Object}
         * @param camada {Object}
         * 
         */         
        function desativarCamada(obj, camada) {
            if (obj) {
                _obterMapa()
                    .then(function (map) {
                        map.removeLayer(obj)

                        if (obj.popup) {
                            map.removeLayer(obj.popup)
                        }
                    });
            }

            if (camada && camada.pontosDaRota && camada.pontosDaRota.router) {
                camada.pontosDaRota.router.setWaypoints([]);
            }
        }

        /**
         * Reinicia o detalhamento
         * 
         * @method resetarDetalhamento
         * 
         * @param layers {Object}
         * 
         */          
        function resetarDetalhamento(layers) {
            var deferred = $q.defer();

            _obterMapa()
                .then(function (map) {
                    window.clearInterval($scope.subItemAtivo);
                    window.clearInterval($scope.poiAtivo);
                    _.each(layers, function (layer) {
                        map.removeLayer(layer);
                    });
                    deferred.resolve();
                });

            return deferred.promise;
        }

        $scope.tweetsAtivos = new L.LayerGroup([]);

        /**
         * Limpa o detalhamento do subItem de um Item aberto no mapa
         * 
         * @method limparSubItem
         * 
         * 
         */             
        function limparSubItem() {
            window.clearInterval($scope.subItemAtivo);
        }

        /**
         * Remove os tweets do mapa
         * 
         * @method limparTweetsAtivos
         * 
         */          
        function limparTweetsAtivos() {
            _obterMapa()
                .then(function (map) {
                    map.removeLayer($scope.tweetsAtivos);
                    $scope.tweetsAtivos.clearLayers();
                });
        }

        /**
         * Ajusta o mapa
         * 
         * @method fitBounds
         * 
         * @param bounds {Object}
         * 
         */          
        function fitBounds(bounds) {
            _obterMapa()
                .then(function (map) {
                    map.fitBounds(bounds, {
                        padding: [50, 150]
                    });
                });
        }

        /**
         * Animação para exibição do tweet
         * 
         * @method twitterFlyTo
         * 
         * @param tweet {Object}
         * 
         */         
        function twitterFlyTo(tweet) {

            _obterMapa()
                .then(function (mapa) {
                    if ($scope.poiAtivo) {
                        window.clearInterval($scope.poiAtivo);
                    }

                    var coords = tweet.geo.coordinates.slice();
                    var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/i;
                    var texto = tweet.text.replace(exp, "<a href='$1' target='_blank'>$1</a>");
                    //coords.reverse();

                    mapa.setView(coords, 17, {
                        animation: true
                    });

                    var t = L.marker(coords, {
                        icon: L.icon({
                            iconSize: [24, 24],
                            iconUrl: '/assets/images/icon_twitter_24.png'
                        })
                    })
                        .bindPopup('@' + tweet.name + ': ' + texto);

                    $scope.tweetsAtivos.addLayer(t);
                    if (!mapa.hasLayer($scope.tweetsAtivos)) {
                        mapa.addLayer($scope.tweetsAtivos);
                    }
                });
        }

        /**
         * Atualiza a camada de tweets
         * 
         * @method adicionarTweets
         * 
         * @param tweets {Object}
         * 
         */              
        function adicionarTweets(tweets) {

            _obterMapa()
                .then(function (map) {
                    map.removeLayer($scope.tweetsAtivos);
                    $scope.tweetsAtivos.clearLayers();
                    var tuites = _.map(tweets, function (tweet) {
                        var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/i;
                        var texto = tweet.text.replace(exp, "<a href='$1' target='_blank'>$1</a>");
                        var coords = tweet.geo.coordinates.slice();
                        var t = L.marker(coords, {
                            icon: L.icon({
                                iconSize: [24, 24],
                                iconUrl: '/assets/images/icon_twitter_24.png'
                            })
                        })
                            .bindPopup('@' + tweet.name + ': ' + texto);

                        return t;
                    });

                    _.each(tuites, function (tuite) {
                        $scope.tweetsAtivos.addLayer(tuite);
                    });
                    map.addLayer($scope.tweetsAtivos);
                });
        }

        /**
         * Remove as camadas recebidas por parâmetros do mapa
         * 
         * @method removerLayers
         * 
         * @param layers {Object}
         * 
         */          
        function removerLayers(layers) {
            _obterMapa()
                .then(function (map) {
                    _.each(layers, function (layer) {
                        map.removeLayer(layer);
                    });
                });
        }

        /* Recebe dois arrays de latLng
         * calcula a distância entre eles e adiciona
         * uma linha entre eles no mapa, retornando uma
         * Promise que resolve com o polyline adicionado no mapa.
         *
         * adicionarLinha([lat, lng], [lat, lng])
         */
        /**
         * Recebe dois arrays de latLng
         * calcula a distância entre eles e adiciona
         * uma linha entre eles no mapa, retornando uma
         * Promise que resolve com o polyline adicionado no mapa.
         * 
         * @method adicionarLinha
         * 
         * @param x {Object}
         * @param y {Object}
         * @param type {Object} 
         * 
         * @returns Promise que resolve com o polyline adicionado no mapa
         */        
        function adicionarLinha(x, y, type) {
            var deferred = $q.defer();

            var distancia;
            var geom = [];

            if (type !== 'Point') {

                if (type == 'Polygon' || type == 'MultiPolygon') {

                    var polygon = turf.polygon(y);
                    geom.push(turf.centroid(polygon).geometry.coordinates[1]);
                    geom.push(turf.centroid(polygon).geometry.coordinates[0]);

                } else {

                    var line = turf.linestring(y);
                    var center = turf.center(line);
                    geom.push(center.geometry.coordinates[1]);
                    geom.push(center.geometry.coordinates[0]);

                }

                distancia = turf.distance(
                    turf.point([x[0], x[1]]),
                    turf.point([geom[0], geom[1]]),
                    'meters');

            } else {
                geom = y;
                distancia = turf.distance(
                    turf.point([x[0], x[1]]),
                    turf.point([y[0], y[1]]),
                    'meters');
            }

            var polyline = L.polyline([x, geom], {
                color: '#FFF',
                opacity: 0.7,
                dashArray: '5, 5',
                weight: 1
            })
                .bindLabel((distancia * 1609).toFixed(0) + ' metros', {
                    noHide: true
                });

            _obterMapa()
                .then(function (map) {
                    polyline.addTo(map);
                    deferred.resolve(polyline);
                });

            return deferred.promise;
        }

        /**
         * Recebe duas coordenadas e adiciona
         * uma linha entre eles no mapa, retornando uma
         * Promise que resolve com o polyline adicionado no mapa.
         * 
         * @method adicionarSubItem
         * 
         * @param pos1 {Object}
         * @param pos2 {Object}
         * @param distancia {Object} 
         * 
         * @returns Promise que resolve com o polyline adicionado no mapa
         */          
        function adicionarSubItem(pos1, pos2, distancia) {
            var deferred = $q.defer();

            adicionarLinha(pos1.coordinates.reverse(), pos2.coordinates.reverse(), pos2.type)
                .then(function (polyline) {
                    if ($scope.subItemAtivo) {
                        window.clearInterval($scope.subItemAtivo);
                    }

                    $scope.subItemAtivo = setInterval(function () {
                        $scope.pingLayer.ping(pos1.coordinates.reverse(), 'pingBlueClass');
                    }, 800);

                    deferred.resolve(polyline);
                });

            return deferred.promise;
        }

        /**
         * Recebe uma coordenada e faz o objeto ficar piscando em azul
         * 
         * @method destacarSubItem
         * 
         * @param pos1 {Object}
         * 
         */           
        function destacarSubItem(pos1) {
            if ($scope.subItemAtivo) {
                window.clearInterval($scope.subItemAtivo);
            }

            $scope.subItemAtivo = setInterval(function () {
                $scope.pingLayer.ping(pos1.coordinates.reverse(), 'pingBlueClass');
            }, 800);

            _obterMapa()
                .then(function (map) {
                    map.setView([pos1.coordinates[1], pos1.coordinates[0]], 17);
                });
        }

        var uwotm8 = _.debounce(getCamerasInViewport, 800);

        /**
         * Listener do Leaflet do atributo moveend 
         * 
         * @method moveend
         * 
         * @param event {Object}
         * @param args {Object} 
         * 
         */          
        $scope.$on('leafletDirectiveMap.moveend', function (event, args) {

            if (uwotm8.then) {
                uwotm8()
                    .then(function (cameras) {
                        CamerasManager.reagir(cameras);
                    });
            }

            if (!$scope.hideCameras) {
                ordenar();
            }

            if (!$scope.ponto)
                $scope.ponto = {};

            obterPontoCentral()
                .then(function (center) {
                    $scope.ponto.lat = center.lat;
                    $scope.ponto.lng = center.lng;
                });
        });

        /*
         * Invalidando tamanho do mapa
         * Fix para bug onde o mapa se perde
         */
        /**
         * Invalidando tamanho do mapa
         * Fix para bug onde o mapa se perde         
         *  
         * @method timeout
         * 
         * 
         */         
        $timeout(function () {
            invalidate();
        }, 1500);

        /*
         * Ao fazer upload de um KML, pegar o retorno
         * em GeoJSON e exibir no mapa
         */
        /**
         *  Ao fazer upload de um KML, pegar o retorno
         * em GeoJSON e exibir no mapa
         * 
         * @method onCompleteItem
         * 
         * @param item {Object}
         * @param response {Object} 
         * 
         */            
        $scope.uploader.onCompleteItem = function (item, response) {
            var geo = L.geoJson(response, {});
            $scope.planejamentoArray.addData(response);
        };

        $scope.uploader.onBeforeUploadItem = function (item) {
            item.url = API_ENDPOINT + 'util/kml/geojson';
        };

        $scope.uploader.alias = 'arquivo';
        $scope.uploader.removeAfterUpload = true;
        $scope.cameras = [];
        $scope.hideCameras = true;
        $scope.hideCameraIcon = 'less';
        $scope.animateClass = 'css-class';
        $scope.hideCameraIcon = 'more';
        $scope.camerasInViewport = [];
        $scope.activePoi = false;

        function checarCamadaAtiva(uuid) { }

      /**
         * Mostra a lista de Câmeras próximas ao ponto clicado 
         * 
         * @method showCameras
         * 
         * @param ponto {Object}
         * @param cameraList {Array}
         * 
         * 
         */            
        function showCameras(ponto, cameraList) {
            $scope.activePoi = true;
            $scope.ponto = ponto;
            $scope.cameras = cameraList;
            $scope.hideCameras = true;
            $scope.$apiMapa.changeClass();
        }

        /**
         *  Faz o objeto nas coordenadas recebidas piscar em vermelho
         * 
         * @method piscarVermelho
         * 
         * @param latLng {Object}
         * 
         */         
        function piscarVermelho(latLng) {
            if (latLng != null && latLng.lat != null && latLng.lng != null) {
                window.clearInterval($scope.poiAtivo);
                var number = 0;
                var stopInterval = $interval(function () {
                    number++;
                    $scope.pingLayer.ping([
                        latLng.lng,
                        latLng.lat
                    ], 'pingRedClass')

                    if (number == 4) {
                        $interval.cancel(stopInterval);
                    }
                }, 800);
                return stopInterval;
            }
        }

        /**
         *  Faz o objeto nas coordenadas recebidas parar de piscar
         * 
         * @method naoPiscar
         * 
         * 
         */                 
        function naoPiscar() {
            window.clearInterval($scope.poiAtivo);
        }

        /**
         *  Faz o objeto nas coordenadas recebidas piscar em azul
         * 
         * @method piscarAzul
         * 
         * @param latLng {Object}
         * 
         */          
        function piscarAzul(latLng) {
            if (latLng != null && latLng.lat != null && latLng.lng != null) {
                window.clearInterval($scope.poiAtivo);

                var number = 0;
                var stopInterval = $interval(function () {
                    number++;
                    $scope.pingLayer.ping([
                        latLng.lng,
                        latLng.lat
                    ], 'pingBlueClass')

                    if (number == 4) {
                        $interval.cancel(stopInterval);
                    }
                }, 800);
            }
        }

        /**
         *  Obtem as cameras em viewport
         * 
         * @method getCamerasInViewport
         * 
         * @returns Câmeras
         * 
         */          
        function getCamerasInViewport() {
            var deferred = $q.defer();

            if (!$scope.ponto)
                $scope.ponto = {};

            obterPontoCentral()
                .then(function (center) {
                    $scope.ponto.lat = center.lat;
                    $scope.ponto.lng = center.lng;
                });

            _obterMapa()
                .then(function (map) {
                    Camera.obterTodas()
                        .then(function (todasCameras) {
                            $scope.totalCameras = todasCameras.length;
                            var todas = _.sortBy(todasCameras, function (camera) {

                                var coords = JSON.parse(camera.properties.geojson).coordinates;
                                var leafletCoords = L.latLng(coords[1], coords[0]);

                                return turf.distance(
                                    turf.point([$scope.ponto.lat, $scope.ponto.lng]),
                                    turf.point([leafletCoords.lat, leafletCoords.lng]),
                                    'miles');
                            });

                            var bounds = map.getBounds();
                            var ne = map.latLngToContainerPoint(bounds._northEast);
                            var sw = map.latLngToContainerPoint(bounds._southWest);
                            var minX = sw.x;
                            var maxX = ne.x;
                            var minY = ne.y;

                            var cameras = _.chain(todas)
                                .filter(function (camera) {
                                    var coords = JSON.parse(camera.properties.geojson).coordinates;
                                    var maxY = ($scope.hideCameras) ? sw.y : sw.y - 145;
                                    var viewPortCams = new Array();
                                    var screenCoords = map.latLngToContainerPoint([coords[1], coords[0]]);
                                    if (screenCoords.x > minX &&
                                        screenCoords.y > minY &&
                                        screenCoords.x < maxX &&
                                        screenCoords.y < maxY) {
                                        return true;
                                    }
                                    return false;
                                })
                                .map(function (camera) {
                                    if (camera.properties.CategoriaCamera.nome === 'SESEG - Genetec') {
                                        camera.properties.numero = 'camera_genetec';
                                    }
                                    return camera;
                                })
                                .value();

                            deferred.resolve(cameras);
                        })

                });

            return deferred.promise;
        }

        /**
         *  Faz a ordenação das câmeras
         * 
         * @method orderCameras
         * 
         * 
         */        
        function orderCameras() {
            //$scope.totalCameras = $scope.cameras.length;
            getCamerasInViewport()
                .then(function (_cameras) {
                    $scope.camerasInViewport = _cameras;
                    $($scope.cameraList).animate({
                        scrollLeft: '-=' + $scope.cameraList.width
                    });
                });
        }

        var ordenar = _.debounce(orderCameras, 200);

        /**
         *  Movimenta a lista de cameras para a direita
         * 
         * @method listBack
         * 
         * 
         */           
        function listBack($event) {
            $($scope.cameraList).animate({
                scrollLeft: '-=100'
            });
        }

        /**
         * Movimenta a lista de cameras para a esquerda
         * 
         * @method listForward
         * 
         * 
         */           
        function listForward($event) {
            $($scope.cameraList).animate({
                scrollLeft: '+=100'
            });
        }

       /**
         * Obtem a instância do mapa
         * 
         * @method _obterMapa
         * 
         * 
         */           
        function _obterMapa() {

            return leafletData
                .getMap(mapaPrincipal)
                .then(function (mapa) {
                    return mapa;
                });
        }

       /**
         * Atualiza a centralização do mapa
         * 
         * @method refreshMap
         * 
         * 
         */            
        function refreshMap() {
            return _obterMapa().then(function (map) {
                map.invalidateSize();
            });
        }

       /**
         * Desenha linhas no mapa que mostram a localização das cameras 
         * 
         * @method drawCameraLines
         * 
         * @param ponto {Object}
         * @param cameraList {Array}
         * 
         * 
         */           
        function drawCameraLines(ponto, cameraList) {
            return _obterMapa()
                .then(function (map) {

                    var cameraLinesArray = [];
                    $scope.closestCamerasCircles = [];

                    for (var i = 0; i < cameraList.length; i++) {
                        var coords = JSON.parse(cameraList[i].geojson).coordinates;
                        var leafletCoords = L.latLng(coords[1], coords[0]);
                        var polyline = L.polyline([ponto, leafletCoords], {
                            color: '#00BBD3',
                            opacity: 1.0,
                            weight: 3
                        });
                        var distancia = turf.distance(turf.point([ponto.lat, ponto.lng]), turf.point([leafletCoords.lat, leafletCoords.lng]), 'miles');
                        polyline.distancia = distancia;
                        polyline.bindLabel((distancia * 1609).toFixed(0) + ' metros', {
                            noHide: false
                        });

                        cameraLinesArray.push(polyline);
                    }

                    $scope.closestCameras = _.sortBy(cameraLinesArray, function (linha) {
                        return linha.distancia;
                    }).slice(0, 3);

                    var closestCamsPoints = [
                        L.marker(L.latLng(ponto.lat, ponto.lng))
                    ];

                    _.each($scope.closestCameras, function (camera) {
                        closestCamsPoints.push(L.marker(L.latLng(camera._latlngs[1].lat, camera._latlngs[1].lng)));
                        camera.addTo(map);
                    });

                    var lg = L.featureGroup(closestCamsPoints);

                    addHistoryView({ ponto: map.getCenter(), zoom: map.getZoom() });
                    map.fitBounds(lg.getBounds());
                });
        }

       /**
         * Desenha linhas no mapa que mostram a localização dos Pois 
         * 
         * @method drawPoiLines
         * 
         * @param ponto {Object}
         * @param poiList {Array}
         * 
         * 
         */         
        function drawPoiLines(ponto, poiList) {
            _obterMapa()
                .then(function (map) {
                    $scope.poiLinesArray = [];

                    _.each(poiList, function (poi) {
                        var coords = JSON.parse(poi.geojson).coordinates;
                        var leafletCoords = L.latLng(coords[1], coords[0]);
                        var polyline = L.polyline([ponto, leafletCoords], {
                            color: '#FFF',
                            opacity: 0.7,
                            dashArray: '5, 5',
                            weight: 1
                        })
                            .bindLabel(poi.distancia + ' metros', {
                                noHide: true
                            })
                            .addTo(map);

                        $scope.poiLinesArray.push(polyline);
                    });

                });
        }

       /**
         * Desenha linhas no mapa que mostram a localização dos Incidentes 
         * 
         * @method drawIncidentesLines
         * 
         * @param ponto {Object}
         * @param incidentesList {Array}
         * 
         * 
         */           
        function drawIncidentesLines(ponto, incidentesList) {
            _obterMapa()
                .then(function (map) {
                    $scope.incidentesLinesArray = new Array();
                    for (var i = 0; i < incidentesList.length; i++) {
                        var coords = JSON.parse(incidentesList[i].geojson).coordinates;
                        var leafletCoords = L.latLng(coords[1], coords[0]);
                        var polyline = L.polyline([ponto, leafletCoords], {
                            color: '#FFF',
                            opacity: 0.7,
                            dashArray: '5, 5',
                            weight: 1
                        }).addTo(map);
                        $scope.incidentesLinesArray.push(polyline);
                    }

                });
        }

       /**
         * Desenha linhas no mapa que mostram a localização das Bases de Conhecimento 
         * 
         * @method drawBasesLines
         * 
         * @param ponto {Object}
         * @param basesList {Array}
         * 
         * 
         */        
        function drawBasesLines(ponto, basesList) {
            _obterMapa()
                .then(function (map) {
                    $scope.basesLinesArray = new Array();
                    for (var i = 0; i < basesList.length; i++) {
                        var coords = JSON.parse(basesList[i].geojson).coordinates;
                        var leafletCoords = L.latLng(coords[1], coords[0]);
                        var polyline = L.polyline([ponto, leafletCoords], {
                            color: '#FFF',
                            opacity: 0.7,
                            dashArray: '5, 5',
                            weight: 1
                        }).addTo(map);

                        basesLinesArray.push(polyline);
                    }

                });
        }

       /**
         * Muda o css das Câmeras para oculta-las 
         * 
         * @method changeClass
         * 
         * @param val {Object}
         * 
         * 
         */           
        function changeClass(val) {
            if (val !== undefined) {
                $scope.hideCameras = val;
            }
            $scope.hideCameras = !$scope.hideCameras;

            if ($scope.hideCameras) {
                $scope.animateClass = 'css-class';
                $scope.hideCameraIcon = 'more';
            } else {
                $scope.animateClass = '';
                $scope.hideCameraIcon = 'less';
                $scope.orderCameras();
            }
        }

       /**
         * Obtem o zoom do mapa 
         * 
         * @method pegarZoom
         * 
         * @param cb {Object}
         * 
         * 
         */         
        function pegarZoom(cb) {
            _obterMapa()
                .then(function (map) {
                    cb(map.getZoom());
                });
        }

        $scope.poisParaRemover = [];
        /**
         * Remove do mapa o Poi clicado e seus Sub Itens 
         * 
         * @method removerPoiClicado
         * 
         * 
         * 
         */        
        function removerPoiClicado() {
            $scope.activePoi = false;
            $scope.camerasInViewport = [];

            _obterMapa()
                .then(function (map) {
                    window.clearInterval($scope.poiAtivo);
                    window.clearInterval($scope.subItemAtivo);
                });
        }

        /**
         * Remove do mapa os linhas que mostram as posições das Câmeras, Incidentes e Poi   
         * 
         * @method removerPoiClicado
         * 
         * 
         * 
         */              
        function removerLinhas() {
            _obterMapa()
                .then(function (map) {

                    _.each($scope.closestCameras, function (camera) {
                        map.removeLayer(camera);
                    });
                    _.each($scope.closestCamerasCircles, function (circle) {
                        map.removeLayer(circle);
                    });
                    _.each($scope.poiLinesArray, function (line) {
                        map.removeLayer(line);
                    });
                    _.each($scope.incidentesLinesArray, function (line) {
                        map.removeLayer(line);
                    });
                    _.each($scope.poisParaRemover, function (poi) {
                        map.removeLayer(poi);
                    });

                });
        }

        /**
         * Remove do mapa os desenhos de Zona de Observação   
         * 
         * @method removerDesenhosZonaObservacao
         * 
         * 
         * 
         */              
        function removerDesenhosZonaObservacao() {
            _obterMapa()
                .then(function (map) {
                    map.eachLayer(function (layer) {
                        if (layer.options && layer.options.bboxAtivado) {
                            map.removeLayer(layer);
                        }
                    });
                });
        }

        /**
         * Remove do mapa a camada de Zona de Observação   
         * 
         * @method removerZonaObservacao
         * 
         * 
         * 
         */          
        function removerZonaObservacao() {
            _obterMapa()
                .then(function (map) {
                    if (map.options.drawControl) {
                        $scope.drawnItems.clearLayers();
                        if (map != null && $scope.drawControl != null) {
                            map.removeControl($scope.drawControl);
                            map.options.drawControl = false;
                        }
                    }
                });
        }

        /**
         * Adiciona no mapa os dados do kml importado   
         * 
         * @method ativarKml
         * 
         * @param url {String}
         * @param cb {Object}
         * 
         */            
        function ativarKml(url, cb) {
            _obterMapa()
                .then(function (map) {
                    var track = omnivore.kml(url);
                    var stylepoly = { fillColor: 'green', fillOpacity: 0.4, color: 'black' };
                    track.addTo(map).on('ready', function () {

                        track.setStyle(stylepoly);

                        track.eachLayer(function (layer) {
                            var leafIcon = L.icon({
                                iconUrl: '/data-s4c/Map-Marker-Marker-Inside-Chartreuse-icon.png',
                                iconSize: new L.Point(38, 95),
                                shadowSize: new L.Point(68, 95),
                                iconAnchor: new L.Point(22, 94),
                                popupAnchor: new L.Point(-3, -76)
                            });
                            var valuePopup = [];
                            Object.keys(layer.feature.properties).forEach(function (key) {
                                var val = layer.feature.properties[key];
                                valuePopup.push(val);
                            });
                            var resultJoin = valuePopup.join(" <br/> ");
                            layer.bindPopup(resultJoin);

                            L.marker(new L.LatLng(50.5, 30.51), { icon: leafIcon }).addTo(map);
                        });
                    });
                    cb(track);
                });
        }

        /**
         * Adiciona no mapa os dados do Shape File importado   
         * 
         * @method ativarShapeFile
         * 
         * @param url
         * @param camada
         * @param cb
         * 
         */            
        function ativarShapeFile(url, camada, cb) {
            _obterMapa()
                .then(function (map) {
                    //var vector = L.geoJson().addTo(map);
                    var vector = L.geoJson([], {
                        style: function (feature) {
                            return feature.properties.style;
                        },
                        onEachFeature: function (feature, layer) {

                            layer.on({
                                click: function (e) {
                                    vector.eachLayer(function (l) {
                                        vector.resetStyle(l);
                                    });

                                    $('.tbodyContent').remove();
                                    var tbody = '<tbody class="tbodyContent">';
                                    for (var key in e.target.feature.properties) {
                                        tbody +=
                                            ('<tr><td style="border: 1px solid #d4d4d5; padding: .7em .8em; font-size: .9em;">' + key + '</td><td style="border: 1px solid #d4d4d5; padding: .7em .8em; font-size: .9em;">' + e.target.feature.properties[key] + '</td></tr>');
                                    }

                                    $mdDialog.show({
                                        templateUrl: 'app/directives/zona-observacao/dialog-shape.html',
                                        controller: function ($scope) {
                                            $scope.res = $scope.$root.res;
                                        }
                                    });

                                    $timeout(function () {
                                        $('#attribute').append(tbody + '</tbody>');
                                        $('#attr').fadeIn(300);
                                        $('#cancelAttr').click(function () {
                                            $mdDialog.hide();
                                        });
                                    }, 500);
                                    map.panTo(e.latlng);

                                    if ('setStyle' in e.target) e.target.setStyle({
                                        fillColor: '#FF0',
                                        fillOpacity: 0.6
                                    });
                                }
                            });
                        }
                    }).addTo(map);

                    try {
                        loadshp({
                            encoding: 'big5',
                            data: camada.kml.data
                        }, function (data) {
                            vector.addData(data);
                            map.fitBounds(vector.getBounds());
                        });
                    } catch (err) {
                        $mdDialog.show(
                            $mdDialog.alert()
                                .title('Erro')
                                .content('Ocorreu um erro ao tentar ativar a camada. Arquivo não é um shape file válido.')
                                .ariaLabel('Ocorreu um erro ao tentar ativar a camada. Arquivo não é um shape file válido.')
                                .ok('OK')
                        );
                    }

                    cb(vector);

                });
        }

       /**
         * Remove as linhas do mapa   
         * 
         * @method clearLines
         * 
         * 
         */  
        function clearLines() {
            $scope.lines.clear();
            $scope.circle.clear();
        }

        $($scope.cameraList).on("scroll", function (e) {
            $scope.$apiMapa.clearLines();
        });

       /**
         * Desenha as linhas do mapa   
         * 
         * @method drawLine
         * 
         * @param $event
         * @param geo
         * 
         */          
        function drawLine($event, geo) {
            if (typeof geo == "string") {
                geo = JSON.parse(geo);
            }

            _obterMapa()
                .then(function (map) {
                    var bounds = map.getBounds();
                    var ne = map.latLngToContainerPoint(bounds._northEast);
                    var sw = map.latLngToContainerPoint(bounds._southWest);
                    var minX = sw.x;
                    var maxX = ne.x;
                    var minY = ne.y;
                    var maxY = ($scope.hideCameras) ? sw.y : sw.y - 145;
                    var stroke = 2;
                    var y = $($event.currentTarget.offsetParent.offsetParent).position().top - $($event.currentTarget).position().top - stroke * 1.5;

                    var x = $($event.currentTarget.offsetParent).position().left + $($event.currentTarget).position().left + $($event.currentTarget).width() / 2 + stroke * 1.5;

                    var screenCoords = map.latLngToContainerPoint([geo.coordinates[1], geo.coordinates[0]]);

                    if (screenCoords.x > minX && screenCoords.y > minY && screenCoords.x < maxX && screenCoords.y < maxY) {
                        $scope.lines.setColor("#FFF");
                        $scope.lines.setStroke(stroke);
                        $scope.lines.drawLine(x, y, screenCoords.x, screenCoords.y);
                        $scope.lines.paint();

                        var r = 10;
                        $scope.circle.setColor("#FFF");
                        $scope.circle.fillEllipse(screenCoords.x - r / 2, screenCoords.y - r / 2, r, r);
                        $scope.circle.paint();
                    }
                });
        }

        function desenharGeometria(geojson) {

        }

       /**
         * Método responsável por centralizar o mapa nas coordenadas recebidas por parâmetro
         * 
         * @method flyTo
         * 
         * @param input
         * 
         */          
        function flyTo(input) {

            var deferred = $q.defer();
            var geo = angular.copy(input);

            if (typeof geo === 'string') {
                geo = JSON.parse(geo);
            }

            if (typeof geo.geometry !== 'undefined') {
                geo = geo.geometry;
            }

            _obterMapa().then(function (map) {

                if ($scope.poiAtivo) {
                    window.clearInterval($scope.poiAtivo);
                }

                if (geo.type == 'Point') {
                    map.setView(geo.coordinates.reverse(), 17, {
                        animation: true,
                        icon: '/data-s4c/teste/ic_incidentes_grey_26px.svg'
                    });

                } else if (geo.type == 'Circle') {
                    var geojson = L.circle({
                        lat: geo.coordinates[1],
                        lng: geo.coordinates[0],
                    }, geo.radius).addTo(map);

                    map.fitBounds(geojson.getBounds());
                    $scope.poisParaRemover.push(geojson);

                } else {
                    var geojson = L.geoJson(geo).addTo(map);
                    map.fitBounds(geojson.getBounds());
                    $scope.poisParaRemover.push(geojson);
                    return deferred.resolve(geojson);
                }

                var poiAtivo = {
                    lat: geo.coordinates[0],
                    lng: geo.coordinates[1]
                };

                piscarAzul(poiAtivo);

                return deferred.resolve(poiAtivo);

            });

            return deferred.promise;
        }


       /**
         * Faz o ajuste do piscar na cor azul
         * 
         * @method blueFitBounds
         * 
         * @param geo1
         * @param geo2
         * 
         */           
        function blueFitBounds(geo1, geo2) {
            _obterMapa()
                .then(function (map) {
                    var lg = L.featureGroup([
                        L.marker(geo1.coordinates.reverse()),
                        L.marker(geo2.coordinates.reverse())
                    ]);

                    if ($scope.subItemAtivo) {
                        window.clearInterval($scope.subItemAtivo);
                    }

                    map.fitBounds(lg.getBounds(), {
                        paddingTopLeft: [100, 100],
                        paddingBottomRight: [100, 200]
                    });

                    $scope.subItemAtivo = setInterval(function () {
                        $scope.pingLayer.ping(geo1.coordinates.reverse(), 'pingBlueClass');
                    }, 800);

                });
        }

       /**
         * Faz o mapa voar para uma coordenada específica e ficar piscando na cor azul
         * 
         * @method blueFlyTo
         * 
         * @param geo {Object}
         * @param camera {Object}
         * 
         */             
        function blueFlyTo(geo, camera) {
            if (typeof geo === 'string') {
                geo = JSON.parse(geo);
            }

            _obterMapa()
                .then(function (map) {

                    if ($scope.subItemAtivo) {
                        window.clearInterval($scope.subItemAtivo);
                    }

                    map
                        .setView(geo.coordinates.reverse(), 17, {
                            animation: true
                        });

                    $scope.subItemAtivo = setInterval(function () {
                        $scope.pingLayer.ping(geo.coordinates.reverse(), 'pingBlueClass');
                    }, 800);

                    clearLines();
                });

            if (camera) {
                camera.CategoriaCameraId = camera.properties.CategoriaCamera.id;
                CamadasService.ativarMenuDaCamera(camera);
            }
        }

        $rootScope.$on('mapa:datamining', function () {
            dataminingViewport();
        });

        /*
         * Procura por alguma layer que tenha ID,
         * se existir, deletar.
         * TODO: Mudar "ID" por algum identificador único
         * de rota.
         */
       /**
         * Remove os desenhos das rotas do mapa
         * 
         * @method removerRotasDesenhadas
         * 
         * 
         */         
        function removerRotasDesenhadas() {
            _obterMapa()
                .then(function (map) {
                    map.eachLayer(function (layer) {
                        if (layer.options.id !== undefined) {
                            map.removeLayer(layer);
                        }
                    });
                });
        }

       /**
         * Remove os pontos das rotas do mapa
         * 
         * @method removerPonto
         * 
         * @param ponto {ponto}
         * 
         */           
        function removerPonto(ponto) {
            if ($scope.router.getWaypoints().length > 2) {
                $scope.router.spliceWaypoints(ponto, 1);
                RotasManager.rotaAtiva.pontos = $scope.router.getWaypoints();
            }
        }

       /**
         * Remove os pontos das rotas do mapa
         * 
         * @method removerPontoRotaUnificada
         * 
         * @param ponto {ponto}
         * 
         */         
        function removerPontoRotaUnificada(ponto) {
            if ($scope.router_unificada.getWaypoints().length > 2 && ponto > 0 && ponto < $scope.router_unificada.getWaypoints().length - 1) {
                $scope.router_unificada.spliceWaypoints(ponto, 1);
                RotasUnificadasManager.rotaAtiva.pontos = $scope.router_unificada.getWaypoints();
            }
        }

       /**
         * Remove a camada de rotas do mapa
         * 
         * @method removerRouter
         * 
         * 
         */            
        function removerRouter() {
            _obterMapa()
                .then(function (map) {
                    $scope.router.setWaypoints([]);
                    map.eachLayer(function (layer) {
                        if (layer.options && layer.options.rota) {
                            map.removeLayer(layer);
                        }
                    });
                });
        }

       /**
         * Remove a camada de rotas do mapa
         * 
         * @method removerRouter
         * 
         * 
         */           
        function removerRouterRotaUnificada() {
            _obterMapa()
                .then(function (map) {

                    if ($scope.router_unificada) {
                        $scope.router_unificada.setWaypoints([]);
                    }

                    map.eachLayer(function (layer) {
                        if (layer.options && layer.options.rotaUnificadaId != null && RotasUnificadasManager.rotaAtiva != null && RotasUnificadasManager.rotaAtiva.id != null && layer.options.rotaUnificadaId == RotasUnificadasManager.rotaAtiva.id) {
                            map.removeLayer(layer);
                        } else if (layer.isKml) {
                            map.removeLayer(layer);
                        }
                    });
                    if (RotasUnificadasManager.rotaAtiva != null) {
                        _.each(RotasUnificadasManager.rotaAtiva.layers_kml, function (layer) {
                            map.removeLayer(layer);
                        });
                    }
                });
        }

       /**
         * Remove a camada de rotas do mapa
         * 
         * @method removerRotasUnificadasDoMapa
         * 
         * 
         */           
        function removerRotasUnificadasDoMapa() {
            _obterMapa()
                .then(function (map) {

                    if ($scope.router_unificada) {
                        $scope.router_unificada.setWaypoints([]);
                    }

                    map.eachLayer(function (layer) {
                        if (layer.options && layer.options.rota) {
                            map.removeLayer(layer);
                        }
                    });
                    if (RotasUnificadasManager.rotaAtiva != null) {
                        _.each(RotasUnificadasManager.rotaAtiva.layers_kml, function (layer) {
                            map.removeLayer(layer);
                        });
                    }
                });
        }

       /**
         * Salva a rota
         * 
         * @method salvarRota
         * 
         * 
         */        
        function salvarRota() {
            var waypoints = [];
            _.reduce($scope.router.getWaypoints(), function (a, sum) {
                waypoints.push({
                    de: a.latLng,
                    para: sum.latLng
                });
                return sum;
            });

            $mdDialog.show({
                templateUrl: 'app/directives/adicionar-rotas/salvar.html',
                controller: 'SalvarRotaCtrl',
                locals: { waypoints: waypoints, res: $scope.res }
            });
        }

       /**
         * Salva a rota
         * 
         * @method salvarRotaUnificada
         * 
         * 
         */         
        function salvarRotaUnificada() {
            var waypoints = [];
            _.reduce($scope.router_unificada.getWaypoints(), function (a, sum) {
                waypoints.push({
                    de: a.latLng,
                    para: sum.latLng
                });
                return sum;
            });

            $mdDialog.show({
                templateUrl: 'app/directives/adicionar-rotas-unificadas/salvar.html',
                controller: 'SalvarRotaUnificadaCtrl',
                locals: { waypoints: waypoints, res: $scope.res }
            });
        }

       /**
         * Atualiza a centralização do mapa
         * 
         * @method invalidate
         * 
         * 
         */          
        function invalidate() {
            _obterMapa()
                .then(function (map) {
                    map.invalidateSize(true);
                });
        }

        $rootScope.$on('s4c:windowResize', function (e, data) {
            $timeout(function () {
                invalidate();
            }, 1500);
        });

       /**
         * Adiciona no mapa uma nova rota default
         * 
         * @method desenharRota
         * 
         * 
         */           
        function desenharRota() {
            _obterMapa()
                .then(function (map) {
                    var bounds = map.getBounds();
                    var latlng = {
                        lat: (bounds._southWest.lat + bounds._northEast.lat) / 2,
                        lng: (bounds._southWest.lng + bounds._northEast.lng) / 2
                    };

                    if ($scope.router !== undefined) {
                        $scope.router.setWaypoints([
                            L.latLng(latlng.lat, latlng.lng + 0.01),
                            L.latLng(latlng.lat, latlng.lng - 0.01)
                        ]);
                    }
                });
        }

       /**
         * Adiciona no mapa uma nova rota default
         * 
         * @method desenharRota
         * 
         * 
         */            
        function desenharRotaUnificada(latlng_) {

            _obterMapa()
                .then(function (map) {

                    if ($scope.router_unificada !== undefined) {
                        if (latlng_ != null) {

                            $scope.router_unificada.setWaypoints(latlng_);
                        }
                        else {
                            var bounds = map.getBounds();
                            var latlng = {
                                lat: (bounds._southWest.lat + bounds._northEast.lat) / 2,
                                lng: (bounds._southWest.lng + bounds._northEast.lng) / 2
                            };
                            $scope.router_unificada.setWaypoints([
                                L.latLng(latlng.lat, latlng.lng + 0.01),
                                L.latLng(latlng.lat, latlng.lng - 0.01)
                            ]);
                        }
                    }
                });
        }

        /**
         * Adiciona no mapa uma nova rota apartir do Kml
         * 
         * @method desenharRotaUnificadaKml
         * 
         * @param pontos {Object}
         * 
         */         
        function desenharRotaUnificadaKml(pontos) {
            _obterMapa()
                .then(function (map) {
                    var waypoints = [];

                    waypoints.push({
                        de: pontos[0],
                        para: pontos[1]
                    });

                    if (RotasUnificadasManager.rotaAtiva.id != null) {
                        RotasUnificadasService.calcularTempoRotaCriada({
                            trechos: waypoints
                        }, RotasUnificadasManager.rotaAtiva.id)
                            .then(function (tempo) {

                                var value = ((100 * (tempo.currentTime / tempo.regularTime)) - 100);

                                if (value <= 10) {
                                    RotasUnificadasManager.rotaAtiva.cor = '#8AC249';
                                } else if (value >= 10 && value <= 50) {
                                    RotasUnificadasManager.rotaAtiva.cor = '#FFFF00';
                                } else {
                                    RotasUnificadasManager.rotaAtiva.cor = '#F34235';
                                }

                                RotasUnificadasManager.rotaAtiva.tempo_atual = tempo.currentTime;
                                RotasUnificadasManager.rotaAtiva.tempo_regular = tempo.regularTime;
                                RotasUnificadasManager.atualizarTempo(RotasUnificadasManager.rotaAtiva);
                            });
                    }
                    else {
                        RotasUnificadasService.calcularTempo({
                            trechos: waypoints
                        })
                            .then(function (tempo) {

                                var value = ((100 * (tempo.currentTime / tempo.regularTime)) - 100);

                                if (value <= 10) {
                                    RotasUnificadasManager.rotaAtiva.cor = '#8AC249';
                                } else if (value >= 10 && value <= 50) {
                                    RotasUnificadasManager.rotaAtiva.cor = '#FFFF00';
                                } else {
                                    RotasUnificadasManager.rotaAtiva.cor = '#F34235';
                                }

                                RotasUnificadasManager.rotaAtiva.tempo_atual = tempo.currentTime;
                                RotasUnificadasManager.rotaAtiva.tempo_regular = tempo.regularTime;
                            });
                    }

                    if (RotasUnificadasManager.rotaAtiva != null && RotasUnificadasManager.rotaAtiva.layers_kml) {
                        _.each(RotasUnificadasManager.rotaAtiva.layers_kml, function (layer) {
                            map.removeLayer(layer);
                        });
                    }

                    RotasUnificadasManager.rotaAtiva.layers_kml = [];

                    var ii = 0;
                    _.each(pontos, function (ponto) {
                        if (ii == 0) {
                            var start_marker = L.marker(ponto.latLng ? ponto.latLng : ponto, {
                                icon: L.icon({
                                    iconUrl: '/assets/images/Rotas/s4c_ic_route_beginning_color1.svg',
                                    iconSize: [48, 48]
                                })
                            });
                            start_marker.isKml = true;
                            start_marker.addTo(map);
                            RotasUnificadasManager.rotaAtiva.layers_kml.push(start_marker);
                        } else {
                            var end_marker = L.marker(ponto.latLng ? ponto.latLng : ponto, {
                                icon: L.icon({
                                    iconUrl: '/assets/images/Rotas/s4c_ic_route_beginning_final.svg',
                                    iconSize: [36, 36]
                                })
                            });
                            end_marker.isKml = true;
                            end_marker.addTo(map);
                            RotasUnificadasManager.rotaAtiva.layers_kml.push(end_marker);
                        }
                        ii++;
                        return;
                    });
                    //}
                });

            obterEnderecoLatLng(0, pontos);

            RotasUnificadasManager.rotaAtiva.kml = true;
        }

        /**
         * Obtem os dados de endereço das coordenadas passadas como parâmetro
         * 
         * @method obterEnderecoLatLng
         * 
         * @param index {Integer}
         * @param pontos {Object}
         * 
         */           
        function obterEnderecoLatLng(index, pontos) {
            if (pontos[index] != null) {
                Geocode.obterEndereco({
                    latitude: pontos[index].latLng ? pontos[index].latLng.lat : pontos[index].lat,
                    longitude: pontos[index].latLng ? pontos[index].latLng.lng : pontos[index].lng
                }).then(function (data) {
                    var rua = data.rua ? data.rua : '';
                    var bairro = data.barrio ? data.barrio : '';
                    var numero = data.numero ? data.numero : '';
                    var cidade = data.cidade ? data.cidade : '';
                    var estado = data.estado ? data.estado : '';
                    var endereco = '' + rua + ' ' + numero + ', ' + bairro + ', ' + cidade + ', ' + estado;

                    RotasUnificadasManager.rotaAtiva.pontos[index].endereco = endereco;

                    index++;
                    obterEnderecoLatLng(index, pontos);
                });
            }
        }

       /**
         * Adiciona no mapa os trajetos da rota
         * 
         * @method desenharTrajetoRotaUnificadaKml
         * 
         * @param trajeto {Object}
         * 
         */           
        function desenharTrajetoRotaUnificadaKml(trajeto) {
            _obterMapa()
                .then(function (map) {
                    var layer = L.geoJson(JSON.parse(JSON.stringify(trajeto)));
                    layer.isKml = true;
                    layer.addTo(map);
                    RotasUnificadasManager.rotaAtiva.layers_kml.push(layer);
                    RotasUnificadasManager.rotaAtiva.trajeto = JSON.stringify(trajeto);
                    if ($scope.router_unificada.getWaypoints() != null && $scope.router_unificada.getWaypoints().length > 0) {
                        $scope.router_unificada.spliceWaypoints(0, 1);
                        $scope.router_unificada.spliceWaypoints(0, 1);
                    }
                });
        }

       /**
         * Obtem a rota ativa
         * 
         * @method obterRotaAtiva
         * 
         */         
        function obterRotaAtiva() {
            return $scope.router;
        }

       /**
         * Obtem a rota ativa
         * 
         * @method obterRotaUnificadaAtiva
         * 
         */             
        function obterRotaUnificadaAtiva() {
            return $scope.router_unificada;
        }

      /**
         * Controle da edição e criação de novas zonas de observação
         * 
         * @method dataminingViewport
         * 
         */             
        function dataminingViewport() {

            _obterMapa().then(function (map) {
                if (!map.options.drawControl) {

                    var bounds = map.getBounds();
                    var obj = {
                        topLeft: bounds._northEast,
                        bottomRight: bounds._southWest
                    };

                    map.options.drawControl = true;
                    $scope.drawnItems = new L.FeatureGroup();
                    map.addLayer($scope.drawnItems);

                    map.on('draw:created', function (e) {
                        if (!$scope.ZonaDeObservacaoManager.ativo) {
                            return;
                        }
                        var layer = e.layer;
                        layer.type = e.layerType;

                        $scope.ZonaDeObservacaoManager.area = layer;

                        layer.editing.enable();
                        $scope.drawnItems.addLayer(layer);
                        map.addLayer(layer);
                        var zonaObservacaoDirective = MainState.getDirective('zonaObservacao');
                        var geojson = layer.toGeoJSON();
                        var center = layer.getBounds().getCenter()

                        if (geojson.geometry.type == 'Point') {
                            geojson.geometry.type = 'Circle';
                            geojson.geometry.radius = layer.getRadius();
                        }

                        geojson.properties.bounds = {
                            topLeft: layer.getBounds().getNorthEast(),
                            bottomRight: layer.getBounds().getSouthWest(),
                            center: [center.lng, center.lat]
                        }

                        zonaObservacaoDirective.buscarDados({
                            geojson: geojson,
                            latitude: layer.getBounds().getCenter().lat,
                            longitude: layer.getBounds().getCenter().lng
                        });

                        layer.on('edit', function () {
                            var layer = e.layer;
                            layer.type = e.layerType;

                            $scope.ZonaDeObservacaoManager.area = layer;

                            layer.editing.enable();
                            $scope.drawnItems.addLayer(layer);
                            map.addLayer(layer);
                            var zonaObservacaoDirective = MainState.getDirective('zonaObservacao');
                            var geojson = layer.toGeoJSON();

                            if (geojson.geometry.type == 'Point') {
                                geojson.geometry.type = 'Circle';
                                geojson.geometry.radius = layer.getRadius();
                            }

                            geojson.properties.bounds = {
                                topLeft: layer.getBounds().getNorthEast(),
                                bottomRight: layer.getBounds().getSouthWest()
                            }

                            zonaObservacaoDirective.buscarDados({
                                geojson: geojson,
                                latitude: layer.getBounds().getCenter().lat,
                                longitude: layer.getBounds().getCenter().lng
                            });

                        });
                    });

                    map.on('draw:drawstart', function (e) {
                        if (!$scope.ZonaDeObservacaoManager.ativo) {
                            return;
                        }
                        /* angular.forEach($scope.drawnItems._layers,function(layer){
                               map.removeLayer(layer);
                             });
                         $scope.drawnItems.clearLayers();*/
                    });
                }
            });
        }

        _obterMapa().then(function (map) {

            map.on('zoomend', function (e) {
                if (!$scope.hideCameras) {
                    $scope.orderCameras();
                }
            });
        });

        _obterMapa()
            .then(function (map) {

                map.on('dragend', function (e) {
                    if (!$scope.hideCameras) {
                        $scope.orderCameras();
                    }
                });
            });

        /**
         * Remove os desenhos do mapa
         * 
         * @method removerDesenho
         * 
         */               
        function removerDesenho() {
            if ($scope.drawnItems) {

                _obterMapa().then(function (map) {
                    angular.forEach($scope.drawnItems._layers, function (layer) {
                        try {
                            map.removeLayer(layer);
                        } catch (err) {
                            $scope.drawnItems = new L.FeatureGroup();
                        }
                    });
                });
                $scope.drawnItems.clearLayers();
            }
        }

        /**
         * Marca a área da Zona de Observação
         * 
         * @method marcarBbox
         * 
         * @param bounds {Object}
         * @param cb {Object}
         * 
         */          
        function marcarBbox(bounds, cb) {
            var NE = bounds._northEast;
            var SW = bounds._southWest;

            var bbox = [NE.lng, NE.lat, SW.lng, SW.lat];
            var resized = turf.size(bbox, 1.1);

            var bboxAtivado = L.rectangle([
                [resized[1], resized[0]],
                [resized[3], resized[2]]
            ], {
                color: "#eeff00",
                weight: 1
            });

            $scope.ZonaDeObservacaoManager.area = bboxAtivado;

            bboxAtivado.options.bboxAtivado = true;

            bboxAtivado.editing.enable();
            bboxAtivado.on('edit', function () {
                var bounds = bboxAtivado.getBounds();
                var obj = {
                    topLeft: bounds._northEast,
                    bottomRight: bounds._southWest
                };
                $scope.ZonaDeObservacaoManager.reload(obj);
            });

            _obterMapa()
                .then(function (map) {

                    map.eachLayer(function (layer) {
                        if (layer.options && layer.options.bboxAtivado) {
                            map.removeLayer(layer);
                        }
                    });

                    map.fitBounds(bboxAtivado);
                    bboxAtivado.addTo(map);
                });
        }

        /**
         * Remove o marker do mapa
         * 
         * @method removerIcon
         * 
         * @param obj {Object}
         * 
         */          
        function removerIcon(obj) {
            _obterMapa()
                .then(function (map) {
                    map.removeLayer(obj.marker);
                });
        }

        /**
         * Adiciona um marker no mapa
         * 
         * @method colocarIcon
         * 
         * @param callback {Function}
         * 
         */          
        function colocarIcon(callback) {
            _obterMapa()
                .then(function (map) {
                    var bounds = map.getBounds();
                    var latlng = {
                        lat: (bounds._southWest.lat + bounds._northEast.lat) / 2,
                        lng: (bounds._southWest.lng + bounds._northEast.lng) / 2
                    };

                    var marker = L.marker([latlng.lat, latlng.lng], {
                        draggable: 'true'
                    }).addTo(map);
                    var pontoInfo = new PontoInfo(marker, 2, marker.getLatLng().lat, marker.getLatLng().lng);
                    callback(pontoInfo);
                });
        }

      /**
         * Atualiza a centralização do mapa
         * 
         * @method gridResized
         * 
         * 
         */           
        function gridResized() {
            $timeout(function () {
                invalidate();
            }, 700);
        }

        /**
         * Remove controles de desenho do módulo de planejamento
         * 
         * @method removerControleDesenho
         * 
         * 
         */            
        function removerControleDesenho() {
            return _obterMapa()
                .then(function (map) {
                    if (map.options.drawControl == true) {
                        map.options.drawControl = false;
                        $scope.planejando = false;
                        PlanejamentoDesenhoService.state.isVisible = false;
                        map.removeControl($scope.drawControl);
                        map.removeControl($scope.saveLayer);
                    }
                });
        }

        /**
         * Remove os pontos da camada de planejamento
         * 
         * @method removerPontosPlanejamento
         * 
         * 
         */             
        function removerPontosPlanejamento() {
            _obterMapa()
                .then(function (map) {
                    $scope.drawnItems.eachLayer(function (layer) {
                        if (layer.popup) {
                            map.removeLayer(layer.popup);
                        }
                    })
                    $scope.drawnItems.clearLayers();
                });
        }

        /* Mostrar menu de adicionar geometria no mapa */
        /**
         * Mostra o menu de adicionar geometria no mapa
         * 
         * @method planejar
         * 
         * @param saveCallBack {Function}
         * @param camada {Object}
         * 
         */           
        function planejar(saveCallBack, camada) {
            if (!saveCallBack)
                saveCallBack = function () {
                    var planejamento = MainState.getDirective('planejamento')
                    planejamento.adicionarCamada(camada ? camada.camadaPlanejamento : null);
                };

            _obterMapa()
                .then(function (map) {
                    if (map.options.drawControl === false) {

                        var saveLayer = L.Control.extend({
                            options: {
                                position: 'topleft'
                            },
                            onAdd: function (map) {
                                var container = L.DomUtil.create('div', 'control-savelayer');

                                L.DomEvent
                                    .addListener(container, 'click', L.DomEvent.stopPropagation)
                                    .addListener(container, 'click', L.DomEvent.preventDefault)
                                    .addListener(container, 'click', saveCallBack);

                                return container;
                            },
                            onRemove: function (map) {
                                this._map = null;
                            }
                        });

                        // adicionando botão de salvar
                        $scope.saveLayer = new saveLayer();
                        map.addControl($scope.saveLayer);

                        // Initialise the FeatureGroup to store editable layers
                        $scope.drawnItems = new L.FeatureGroup();

                        if (camada) {
                            map.removeLayer(camada.layer);

                            camada.layer.eachLayer(function (layer) {
                                layer.off('mouseover mousemove mouseout')
                                layer.editing.disable();
                                $scope.drawnItems.addLayer(layer);

                                if (PlanejamentoDesenhoService.iconePlanejamento) {
                                    layer.options.type = PlanejamentoDesenhoService.iconePlanejamento.type;
                                }

                                PlanejamentoDesenhoService.definirLayerAtual(layer, map);
                                PlanejamentoDesenhoService.on('iconChange', function (layerIcon) {
                                    if (PlanejamentoDesenhoService.iconePlanejamento.icon) {
                                        PlanejamentoDesenhoService.layerAtual.setIcon(PlanejamentoDesenhoService.iconePlanejamento.icon);
                                    }
                                });
                            })
                        }

                        map.addLayer($scope.drawnItems);

                        L.Planejamento = L.DrawToolbar.extend({
                            options: {
                                position: 'topleft'
                            }
                        });

                        L.Planejamento.include({
                            getModeHandlers: function (map) {
                                var ret = [];

                                ret.push({
                                    enabled: this.options.polyline,
                                    handler: new L.Draw.Polyline(map, this.options.polyline),
                                    title: L.drawLocal.draw.toolbar.buttons.polyline
                                });

                                ret.push({
                                    enabled: this.options.polygon,
                                    handler: new L.Draw.Polygon(map, this.options.polygon),
                                    title: L.drawLocal.draw.toolbar.buttons.polygon
                                });

                                ret.push({
                                    enabled: this.options.rectangle,
                                    handler: new L.Draw.Rectangle(map, this.options.rectangle),
                                    title: L.drawLocal.draw.toolbar.buttons.rectangle
                                });

                                ret.push({
                                    enabled: this.options.bombeiro,
                                    handler: new L.Draw.Bombeiro(map, {
                                        icon: PlanejamentoDesenhoService.iconePlanejamento.icon,
                                        handler: PlanejamentoDesenhoService
                                    }),
                                    title: ''
                                });

                                return ret;
                            }
                        });

                        L.Control.CustomDraw = L.Control.Draw.extend({
                            initialize: function (options) {
                                L.Control.prototype.initialize.call(this, options);

                                var toolbar;

                                this._toolbars = {};

                                // Initialize toolbars
                                if (L.Planejamento && this.options.draw) {
                                    toolbar = new L.Planejamento(this.options.draw);

                                    this._toolbars[L.DrawToolbar.TYPE] = toolbar;

                                    // Listen for when toolbar is enabled
                                    this._toolbars[L.DrawToolbar.TYPE].on('enable', this._toolbarEnabled, this);
                                }

                                if (L.EditToolbar && this.options.edit) {
                                    toolbar = new L.EditToolbar(this.options.edit);

                                    this._toolbars[L.EditToolbar.TYPE] = toolbar;

                                    // Listen for when toolbar is enabled
                                    this._toolbars[L.EditToolbar.TYPE].on('enable', this._toolbarEnabled, this);
                                }
                            }
                        });


                        // Initialise the draw control and pass it the FeatureGroup of editable layers
                        $scope.drawControl = new L.Control.CustomDraw({
                            position: 'topleft',
                            draw: {
                                polygon: {
                                    shapeOptions: {
                                        color: 'red'
                                    },
                                    allowIntersection: false,
                                    drawError: {
                                        color: 'orange',
                                        timeout: 1000
                                    },
                                    showArea: true,
                                    metric: false,
                                    repeatMode: false
                                },
                                polyline: {
                                    shapeOptions: {
                                        color: 'red'
                                    }
                                },
                                rect: {
                                    shapeOptions: {
                                        color: 'green'
                                    }
                                },
                                circle: {
                                    shapeOptions: {
                                        color: 'steelblue'
                                    }
                                },
                                bombeiro: true,
                                rotas: true
                            },
                            edit: {
                                edit: false,
                                remove: false,
                                featureGroup: $scope.drawnItems
                            }
                        });

                        map.addControl($scope.drawControl);
                        map.options.drawControl = true;

                        $scope.planejando = true;
                        $scope.planejamentoArray = L.geoJson();
                        //$scope.router.setWaypoints([]);

                        map.on('draw:created', function (e) {

                            if (!$scope.planejando) {
                                return;
                            }

                            var type = e.layerType,
                                layer = e.layer;

                            layer.editing.disable();

                            if (PlanejamentoDesenhoService.iconePlanejamento) {
                                layer.options.type = PlanejamentoDesenhoService.iconePlanejamento.type;
                            }

                            PlanejamentoDesenhoService.definirLayerAtual(layer, map);
                            PlanejamentoDesenhoService.on('iconChange', function (layerIcon) {
                                if (PlanejamentoDesenhoService.iconePlanejamento.icon) {
                                    PlanejamentoDesenhoService.layerAtual.setIcon(PlanejamentoDesenhoService.iconePlanejamento.icon);
                                }
                            });

                            $scope.drawnItems.addLayer(layer);
                        });

                        map.on('draw:drawstart', function (e) {
                            if (!$scope.planejando) {
                                return;
                            }
                            var offsetX = $scope.drawControl._container.offsetLeft;
                            var offsetY = $scope.drawControl._container.offsetTop;
                            PlanejamentoDesenhoService.calcularPosicaoDesenho(offsetX, offsetY, e.layerType);
                            PlanejamentoDesenhoService.triggerNovoLayer(e.layerType);
                            PlanejamentoDesenhoService.clearListeners('iconChange');
                        });

                        map.on('draw:edited', function (e) {
                            if (!$scope.planejando) {
                                return;
                            }
                            PlanejamentoDesenhoService.currentLayer = e.layer;
                        });
                    }
                });
        }

        /**
         * Remove o desenho da camada de planejamento do mapa
         * 
         * @method removerDesenhoPlanejamento
         * 
         * @param layer {Object}
         * 
         */           
        function removerDesenhoPlanejamento(layer) {
            $scope.drawnItems.removeLayer(layer);
            _obterMapa()
                .then(function (map) {
                    map.removeLayer(layer);
                });
        }

        /**
         * Retorna os pontos da rota
         * 
         * @method pegarPontosDaRota
         * 
         * 
         */            
        function pegarPontosDaRota() {
            var waypoints = _.filter($scope.router.getWaypoints(), _.property('latLng'));

            return _.map(waypoints, function (coordenada) {
                return {
                    latitude: coordenada.latLng.lat,
                    longitude: coordenada.latLng.lng,
                    endereco: coordenada.name
                }
            });
        }

        /**
         * Retorna os pontos da rota
         * 
         * @method pegarPontosDaRotaUnificada
         * 
         * 
         */          
        function pegarPontosDaRotaUnificada() {
            var waypoints = _.filter($scope.router_unificada.getWaypoints(), _.property('latLng'));

            return _.map(waypoints, function (coordenada) {
                return {
                    latitude: coordenada.latLng.lat,
                    longitude: coordenada.latLng.lng,
                    endereco: coordenada.name
                }
            });
        }

         /**
         * Recupera geometrias do Planejamento desenhadas no mapa
         * 
         * @method pegarGeometrias
         * 
         * 
         */         
        function pegarGeometrias() {

            $scope.planejamentoArray = [];
            $scope.drawnItems.eachLayer(function (layer) {

                var geoJSON = layer.toGeoJSON();
                geoJSON.style = {
                    'color': layer.options.color,
                    'fillColor': layer.options.fillColor,
                    'weight': layer.options.weight,
                    'opacity': layer.options.opacity
                };

                if (layer.options.icon) {
                    geoJSON.properties.icon = angular.copy(layer.options.icon.options);
                    geoJSON.properties.quantidade = layer.options.quantidade;
                }

                if (layer.options.texto) {
                    geoJSON.properties.texto = layer.options.texto;
                }

                if (layer instanceof L.Circle) {
                    geoJSON.properties.type = 'Circle';
                    geoJSON.properties.radius = layer.getRadius();
                }

                $scope.planejamentoArray.push(geoJSON);
            });

            var fc = turf.featurecollection($scope.planejamentoArray);
            return fc;
        }

        /**
         * Desenha um raio ao redor da localização do Twitter
         * 
         * @method desenharRaioTwitter
         * 
         * 
         */           
        function desenharRaioTwitter() {

            var ponto = JSON.parse(DetalhamentoManager.data.geojson);
            var latitude = ponto.coordinates[1];
            var longitude = ponto.coordinates[0];

            _obterMapa().then(function (map) {
                $scope.areaTwitter = L.circle([
                    latitude,
                    longitude
                ], 2000, {
                    color: '#eeff00',
                    weight: 1
                });

                $scope.areaTwitter.addTo(map);
                $scope.areaTwitter.editing.enable();

                var twitter = MainState.getDirective('twitter') || {};

                /*
                 * Atualizando a área da diretiva do twitter
                 * (acessa diretamente a API da diretiva)
                 */
                twitter.atualizarArea({
                    area: $scope.areaTwitter,
                    latitude: $scope.areaTwitter.getLatLng().lat,
                    longitude: $scope.areaTwitter.getLatLng().lng,
                    distancia: $scope.areaTwitter.getRadius() / 1000
                });

                $scope.areaTwitter.on('edit', function () {
                    twitter.atualizarArea({
                        area: $scope.areaTwitter,
                        latitude: $scope.areaTwitter.getLatLng().lat,
                        longitude: $scope.areaTwitter.getLatLng().lng,
                        distancia: $scope.areaTwitter.getRadius() / 1000
                    });
                });
            });
        }

        /**
         * Remove o raio ao redor da localização do Twitter
         * 
         * @method desenharRaioTwitter
         * 
         * 
         */          
        function removerRaioTwitter() {
            _obterMapa().then(function (map) {
                map.removeLayer($scope.areaTwitter);
            });
        }

        /**
         * Abre a legenda da representação
         * 
         * @method abrirLegenda
         * 
         * @param $event {Object}
         * 
         */          
        function abrirLegenda($event) {
            $mdBottomSheet.show({
                templateUrl: 'app/directives/mapa/legendas.html',
                //parent: $('#mapaPrincipal'),
                controller: function ($scope, $mdBottomSheet, CamadasService, $state, $window) {

                    $scope.res = $scope.$root.res;

                    $scope.fecharLegenda = $mdBottomSheet.cancel;

                    var categorias = [];

                    $scope.representacoes = CamadasService.representacoes_das_Categorias_Ativas;

                    $scope.abrirDetalhamentoLegenda = function (representacao_id) {
                        //var popupWindow = window.open('#/Legenda/'+representacao_id+'?token=' + sessionStorage.s4cToken, 'Legenda', 'toolbar=no,location=no,status=no,menubar=no,scrollbars=false,resizable=false,width=640,height=480');
                        var representacao_param = _.find($scope.representacoes, function (representacao_) { return representacao_.id == representacao_id });
                        var tamJanela = 640;
                        if (representacao_param.json.legenda_completa != null && representacao_param.json.legenda_completa.length > 0) {
                            tamJanela = 250 * representacao_param.json.legenda_completa.length;
                        }
                        var newWindowRef = $window.open('#/Legenda/' + representacao_id + '?token=' + sessionStorage.s4cToken, "Legenda", 'toolbar=no,location=no,status=no,menubar=no,scrollbars=false,resizable=false,width=' + tamJanela + ',height=480');
                        $window.ScopeToShare = $scope;
                    }

                    $scope.trustAsResourceUrl = $sce.trustAsResourceUrl;
                    $('#s4c-help')[0].style.display = 'none';
                    $('#s4c-bottom-sheet-cameras')[0].style.display = 'flex';

                }
            }).then(function (argument) {
                $('#s4c-help')[0].style.display = 'flex';
            }, function () {
                // Eu sei que é estranho mas se você não seleciona nada e fecha ele cai aqui
                // Como na legenda não vai selecionar nada ficou assim.
                $('#s4c-help')[0].style.display = 'flex';
            });
        }

      /**
         * Verifica se a Categoria possui url para ícone
         * 
         * @method possuiIcones
         * 
         * @param acervo {Object}
         * 
         */          
        function possuiIcones(acervo) {
            return _.some(acervo.Categoria, function (categoria) {
                return categoria.urlIcone;
            })
        }

      /**
         * Define a cor da linha dos desenhos das geometrias do mapa
         * 
         * @method definirCorDaLinha
         * 
         * @param cor {String}
         * 
         */         
        function definirCorDaLinha(cor) {
            _obterMapa()
                .then(function (map) {
                    map.removeControl($scope.drawControl);
                    $scope.drawControl = new L.Control.Draw({
                        position: 'topleft',
                        draw: {
                            polygon: {
                                shapeOptions: {
                                    color: cor
                                },
                                allowIntersection: false,
                                drawError: {
                                    color: cor,
                                    timeout: 1000
                                },
                                showArea: true,
                                metric: false,
                                repeatMode: true
                            },
                            polyline: {
                                shapeOptions: {
                                    color: cor
                                }
                            },
                            rectangle: {
                                shapeOptions: {
                                    color: cor
                                }
                            },
                            circle: {
                                shapeOptions: {
                                    color: cor
                                }
                            },
                            policia: true,
                            bombeiro: true,
                            pmv: true,
                            bloqueio: true,
                            cone: true

                        },
                        edit: {
                            featureGroup: $scope.drawnItems
                        }
                    });
                    map.addControl($scope.drawControl);
                });
        }

      /**
         * Faz o desenho da geometria contido no GeoJson e inclui no mapa
         * 
         * @method desenharFromJSON
         * 
         * @param geojson {Json}
         * @param alarmeData {Object}
         * 
         */          
        function desenharFromJSON(geojson, alarmeData) {
            _obterMapa()
                .then(function (map) {
                    var area;
                    if (geojson.geometry.type === 'Circle') {
                        area = L.circle(
                            [geojson.geometry.coordinates[1], geojson.geometry.coordinates[0]],
                            geojson.geometry.radius, {
                            color: '#eeff00',
                            weight: 1,
                            bboxAtivado: true
                        });
                    } else {
                        area = L.geoJson(geojson, {
                            style: function (feature) {
                                return {
                                    color: '#eeff00',
                                    weight: 1,
                                    bboxAtivado: true
                                }
                            }
                        });
                    }
                    area.addTo(map);
                    $scope.ZonaDeObservacaoManager.area = area;

                    if (alarmeData != null && alarmeData.tweets && alarmeData.tweets.length > 1) {
                        var fc = {
                            type: "FeatureCollection",
                            features: []
                        };
                        _.each(_.map(_.map(alarmeData.tweets, 'geo'), 'coordinates'), function (coord) {
                            if (coord) {
                                fc.features.push({
                                    type: "Feature",
                                    properties: {},
                                    geometry: {
                                        type: "Point",
                                        coordinates: [coord[0], coord[1]]
                                    }
                                });
                            }
                        })
                        var bbox = turf.extent(fc);
                        map.fitBounds(L.latLngBounds([bbox[2], bbox[3]], [bbox[0], bbox[1]]));
                    }
                    else {
                        map.fitBounds(area.getBounds());
                    }
                });
        }

     /**
         * Faz o desenho das regiões da Área de Atuação para incluir no mapa
         * 
         * @method desenharRegioes
         * 
         * @param areaAtuacao {Object}
         * @param idRegiao {Integer}
         * @param abreJanela {Function}
         * 
         */         
        function desenharRegioes(areaAtuacao, idRegiao, abreJanela) {
            var deferred = $q.defer();
            var arrayLayers = [];

            _obterMapa()
                .then(function (map) {

                    _.each(areaAtuacao.regioes, function (regiao) {
                        var geometry = JSON.parse(regiao.poiGeometrico);
                        var layerRegiao = L.geoJson(geometry).addTo(map);

                        layerRegiao.eachLayer(function (layer) {
                            layer.on({
                                click: function () {
                                    abreJanela(regiao.id, layer);
                                }
                            });
                            layer.setStyle({
                                weight: 2,
                                color: areaAtuacao.cor,
                                dashArray: '',
                                fillOpacity: (regiao.id === idRegiao ? 0.8 : 0.4),
                                opacity: 1
                            });
                        });

                        layerRegiao.idArea = areaAtuacao.id;
                        layerRegiao.idRegiao = regiao.id;
                        arrayLayers.push(layerRegiao);
                    });

                    deferred.resolve(arrayLayers);

                });

            return deferred.promise;
        }


     /**
         * Cria uma movimentação animada para mostrar no mapa a nova posição do Ponto Móvel
         * 
         * @method atualizarPosicaoPontoMovel
         * 
         * @param erInfo {Object}
         * 
         */           
        function atualizarPosicaoPontoMovel(erInfo) {
            _obterMapa()
                .then(function (map) {

                    var pontoMovel;
                    var posicaoAtual;
                    //Remove o layer atual
                    if ($scope.PontoMovelManager.layersPontoMovel != null) {
                        _.each($scope.PontoMovelManager.layersPontoMovel.getLayers(), function (layer) {
                            if (layer.pontoMovel.idElementoRastreavel == erInfo.elementoRastreavelId) {
                                pontoMovel = layer.pontoMovel;
                                posicaoAtual = layer;
                                map.removeLayer(layer);
                            }
                        });
                    }

                    if (pontoMovel == undefined) {
                        return;
                    }

                    var ponto = {
                        lat: erInfo.latitude,
                        lng: erInfo.longitude
                    };

                    removerLayerPontoMovel(map, pontoMovel.id, 'ER');

                    var trajeto = [[posicaoAtual._latlng.lat, posicaoAtual._latlng.lng], [ponto.lat, ponto.lng]];

                    var animatedMarker = L.Marker.movingMarker(trajeto, [1000], {
                        icon: L.icon({
                            iconUrl: pontoMovel.tipo.urlIcone,
                            iconSize: [32, 32]
                        })
                    });

                    animatedMarker.on('click', function (e) {
                        PontoMovelManager.abrirPontoMovel(pontoMovel.id, ponto);
                    });

                    animatedMarker.pontoMovel = pontoMovel;
                    animatedMarker.tipoLayer = 'pontoMovel';
                    animatedMarker.bindLabel(pontoMovel.nome, {
                        permanent: true,
                        direction: 'right'
                    });
                    map.addLayer(animatedMarker);

                    animatedMarker.start();

                    if ($scope.PontoMovelManager.pontoMovelAtivo != null && $scope.PontoMovelManager.pontoMovelAtivo.id == pontoMovel.id) {
                        piscarAzul(ponto);
                    }
                    $scope.PontoMovelManager.layersPontoMovel.addLayer(animatedMarker);
                    $scope.PontoMovelManager.pontoMovelMarkerAtivo = animatedMarker;
                });
        }

        
     /**
         * Desenha a geometria do Ponto Móvel para adicionar no mapa
         * 
         * @method desenharPontoMovel
         * 
         * @param pontoMovel {Object}
         * @param ponto {Object}
         * 
         */                
        function desenharPontoMovel(pontoMovel, ponto) {
            _obterMapa()
                .then(function (map) {

                    var ponto_movel_marker = L.marker(ponto, {
                        icon: L.icon({
                            iconUrl: pontoMovel.tipo.urlIcone,
                            iconSize: [32, 32]
                        })
                    });
                    ponto_movel_marker.on('click', function (e) {
                        PontoMovelManager.abrirPontoMovel(pontoMovel.id, ponto);
                    });
                    ponto_movel_marker.bindLabel(pontoMovel.nome,
                        {
                            permanent: true,
                            direction: 'right'
                        });

                    ponto_movel_marker.pontoMovel = pontoMovel;
                    ponto_movel_marker.tipoLayer = 'pontoMovel';
                    map.addLayer(ponto_movel_marker);
                    if ($scope.PontoMovelManager.pontoMovelAtivo != null && $scope.PontoMovelManager.pontoMovelAtivo.id == pontoMovel.id) {
                        piscarAzul(ponto);
                        map.setView(ponto, 16);
                        PontoMovelManager.abrirPontoMovel(pontoMovel.id, ponto);
                    }
                    $scope.PontoMovelManager.layersPontoMovel.addLayer(ponto_movel_marker);
                    $scope.PontoMovelManager.pontoMovelMarkerAtivo = ponto_movel_marker;
                });
        }

     /**
         * Desenha a geometria dos dispositivos de rastreamento para adicionar no mapa
         * 
         * @method desenharERs
         * 
         * @param listERInfo {Object}
         * @param posicaoAtualER {Object}
         * @param pontoMovel {Object}
         * 
         */           
        function desenharERs(listERInfo, posicaoAtualER, pontoMovel) {
            _obterMapa()
                .then(function (map) {

                    removerLayerPontoMovel(map, pontoMovel.id, 'ER');

                    var linha = [];

                    _.each(listERInfo, function (erInfo) {

                        var ponto = {
                            lat: erInfo.latitude,
                            lng: erInfo.longitude
                        };

                        var posicaoER = L.marker(ponto, {
                            icon: L.icon({
                                iconUrl: getIconeDispositivo(pontoMovel.tipo.iconesDispositivo, erInfo.dispositivoRastreamento.tipo.id),
                                iconSize: [16, 16]
                            })
                        });

                        posicaoER.bindLabel(pontoMovel.nome + ' - ' + erInfo.dispositivoRastreamento.nome,
                            {
                                permanent: true,
                                direction: 'right'
                            });

                        posicaoER.pontoMovel = pontoMovel;
                        posicaoER.tipoLayer = 'ER';
                        map.addLayer(posicaoER);
                        linha.push([posicaoAtualER.lat, posicaoAtualER.lng]);
                        linha.push([erInfo.latitude, erInfo.longitude]);
                        $scope.PontoMovelManager.subItens.addLayer(posicaoER);
                    });

                    var polylines = L.polyline(linha);
                    polylines.pontoMovel = pontoMovel;
                    polylines.tipoLayer = 'ER';
                    polylines.addTo(map);
                    $scope.PontoMovelManager.subItens.addLayer(polylines);

                    map.setView([posicaoAtualER.lat, posicaoAtualER.lng], 18);
                });
        }

     /**
         * Retorna a url do Ícone do dispositivo
         * 
         * @method getIconeDispositivo
         * 
         * @param iconesDispositivo {Object}
         * @param id {Object}
         * 
         */             
        function getIconeDispositivo(iconesDispositivo, id) {

            var urlIcone;

            _.each(iconesDispositivo, function (iconeDispositivo) {
                if (iconeDispositivo.idDispositivo == id) {
                    urlIcone = iconeDispositivo.urlIcone;
                }
            });

            return urlIcone;
        }

     /**
         * Desenha a geometria do percursso feito pelo dispositivo de rastreamento
         * 
         * @method desenharPercursoER
         * 
         * @param listHistoricoPosicoesER {Object}
         * @param posicaoAtualER {Object}
         * @param pontoMovel {Object}
         * 
         */             
        function desenharPercursoER(listHistoricoPosicoesER, posicaoAtualER, pontoMovel) {

            if (listHistoricoPosicoesER.length == 0) {
                return;
            }

            _obterMapa()
                .then(function (map) {

                    removerLayerPontoMovel(map, pontoMovel.id, 'Percurso');
                    var linha = [];
                    var index = 0;
                    var primeiraPosicao;

                    _.each(listHistoricoPosicoesER, function (erInfo) {

                        linha.push([erInfo.latitude, erInfo.longitude]);

                        if (index == 0) {
                            primeiraPosicao = erInfo;
                        }

                        var posicaoER = L.marker([erInfo.latitude, erInfo.longitude], {
                            icon: L.icon({
                                iconUrl: index < listHistoricoPosicoesER.length - 1 ? '/assets/images/Rotas/s4c_ic_route_beginning_color1.svg' : '/assets/images/Rotas/s4c_ic_route_beginning_final.svg',
                                iconSize: [48, 48]
                            })
                        });

                        posicaoER.bindLabel(pontoMovel.nome + ' - ' + erInfo.dataPosicaoStr,
                            {
                                permanent: true,
                                direction: 'right'
                            });

                        posicaoER.pontoMovel = pontoMovel;
                        posicaoER.tipoLayer = 'Percurso';
                        map.addLayer(posicaoER);
                        $scope.PontoMovelManager.subItens.addLayer(posicaoER);
                        index++;
                    });

                    if (listHistoricoPosicoesER.length > 1) {
                        var decorator = L.polylineDecorator(linha, {
                            patterns: [
                                { offset: 25, repeat: 60, symbol: L.Symbol.arrowHead({ pixelSize: 15, pathOptions: { color: '#3366ff', fillOpacity: 1, weight: 0 } }) }
                            ]
                        });

                        decorator.pontoMovel = pontoMovel;
                        decorator.tipoLayer = 'Percurso';
                        decorator.addTo(map)

                        $scope.PontoMovelManager.subItens.addLayer(decorator);
                    }
                    map.setView([primeiraPosicao.latitude, primeiraPosicao.longitude], 12);
                });
        }

     /**
         * Remove a camada do ponto móvel do mapa
         * 
         * @method removerLayerPontoMovel
         * 
         * @param map {Object}
         * @param idPontoMovel {Object}
         * @param tipoLayer {Object}
         * 
         */         
        function removerLayerPontoMovel(map, idPontoMovel, tipoLayer) {
            if ($scope.PontoMovelManager.subItens != null) {
                _.each($scope.PontoMovelManager.subItens.getLayers(), function (layer) {
                    if (layer.pontoMovel.id == idPontoMovel && layer.tipoLayer == tipoLayer) {
                        map.removeLayer(layer);
                    }
                });
            }
        }

        /**
         * Define o centro do mapa e o nível de zoom
         * 
         * @method setView
         * 
         * @param ponto {Object}
         * @param zoom {Object}
         * @param noView {Object}
         * 
         */          
        function setView(ponto, zoom, noView) {

            _obterMapa()
                .then(function (map) {

                    if (!noView) {
                        addHistoryView({ ponto: map.getCenter(), zoom: map.getZoom() });
                    }
                    map.setView(ponto, zoom);
                });
        }

        /**
         * Desenha uma geometria circular ao redor do Poi
         * 
         * @method desenharCirculosPois
         * 
         * @param featureCollection {Object}
         * @param radius {Object}
         * 
         */              
        function desenharCirculosPois(featureCollection, radius) {
            var deferred = $q.defer();
            var arrayCircles = [];

            _obterMapa()
                .then(function (map) {

                    for (var i = 0; i < featureCollection.features.length; i++) {
                        var feature = featureCollection.features[i];
                        var circle = L.circle({
                            lat: feature.properties.latitude,
                            lng: feature.properties.longitude,
                        }, radius, {
                            color: '#ff0000',
                            fill: true,
                            opacity: 0.5,
                            weight: 2
                        }).addTo(map);

                        circle.categoriaId = featureCollection.categoriaId;
                        arrayCircles.push(circle);
                    }

                    deferred.resolve(arrayCircles);
                });

            return deferred.promise;
        }

        /**
         * Remove o desenho de geometria circular ao redor do Poi
         * 
         * @method removerCirculosPois
         * 
         * @param arrayCircles {Object}
         * @param categoriaId {Object}
         * 
         */                 
        function removerCirculosPois(arrayCircles, categoriaId) {

            _obterMapa()
                .then(function (map) {

                    for (var i = 0; i < arrayCircles.length; i++) {
                        if (categoriaId == null || arrayCircles[i].categoriaId == categoriaId) {
                            map.removeLayer(arrayCircles[i]);
                        }
                    }
                });
        }

        /**
         * Organiza os Pois relacionados para que fiquem enquadrados mapa
         * 
         * @method enquadrarPoisRelacionados
         * 
         * @param arrayCircles {Object}
         * 
         */          
        function enquadrarPoisRelacionados(arrayCircles) {

            _obterMapa()
                .then(function (map) {
                    var arrayPoints = [];

                    for (var i = 0; i < arrayCircles.length; i++) {
                        var circle = arrayCircles[i];
                        arrayPoints.push(L.marker(L.latLng(circle._latlng.lat, circle._latlng.lng)));
                    }

                    var lg = L.featureGroup(arrayPoints);
                    map.fitBounds(lg.getBounds());
                });
        }

        var coordsIniciais = {
            latitude: -22.94249,
            longitude: -43.48789,
            zoom: 10
        };


        if ($rootScope.ParametrosS4C.hasOwnProperty('mapaPosInicial') &&
            typeof $rootScope.ParametrosS4C.mapaPosInicial === 'object' &&
            $rootScope.ParametrosS4C.mapaPosInicial != null) {

            coordsIniciais.latitude = parseFloat("STA01.02".replace(/[^0-9\.]+/g, $rootScope.ParametrosS4C.mapaPosInicial.coordinates.coordinates[0]));
            coordsIniciais.longitude = parseFloat("STA01.02".replace(/[^0-9\.]+/g, $rootScope.ParametrosS4C.mapaPosInicial.coordinates.coordinates[1]));
            coordsIniciais.zoom = $rootScope.ParametrosS4C.mapaPosZoom;

        }

        $scope.views = [];
        $scope.backButton;
        $scope.advanceButton;
        $scope.viewIndex = 0;

       /**
         * Adiciona a posição atual do mapa no histórico de visualização
         * 
         * @method addHistoryView
         * 
         * @param view {Object}
         * 
         */          
        function addHistoryView(view) {

            if ($scope.views.length == 0) {

                var ourCustomControl = L.Control.extend({

                    options: {
                        position: 'topright'
                        //control position - allowed: 'topleft', 'topright', 'bottomleft', 'bottomright'
                    },

                    onAdd: function (map) {
                        var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom');

                        container.style.backgroundColor = 'white';
                        container.style.width = '30px';
                        container.style.height = '30px';
                        container.style.cursor = 'pointer'
                        container.title = $scope.res('VOLTAR_MAPA');
                        container.innerHTML = '<img src="assets/images/left_arrow.svg" style="margin-top:4px; margin-left:4px; width:22px; height:22px;"/>';

                        container.onclick = backEvent;
                        $scope.backButton = container;
                        return container;
                    },

                });

                _obterMapa().then(function (map) {
                    new ourCustomControl().addTo(map);
                });
            }

            if ($scope.backButton) {
                $scope.backButton.style.backgroundColor = 'white';
            }

            if (containsView(view)) {
                return;
            }

            if ($scope.views.length >= 4) {
                $scope.views.splice($scope.views.length - 1, 1);
            }

            $scope.views.unshift(view);
            $scope.viewIndex = -1;
        }

       /**
         * Verifica se a posição passada por parâmetro está no histórico de visualização
         * 
         * @method containsView
         * 
         * @param view {Object}
         * 
         */          
        function containsView(view) {

            if ($scope.views.length == 0) {
                return false;
            }

            for (var index in $scope.views) {
                if ($scope.views[index].zoom == view.zoom && $scope.views[index].ponto.lat == view.ponto.lat && $scope.views[index].ponto.lng == view.ponto.lng) {
                    return true;
                }
            }

            return false;
        }

       /**
         * Volta o mapa para a posição anterior
         * 
         * @method backEvent
         * 
         * @param event {Object}
         * 
         */         
        function backEvent(event) {

            event.stopPropagation();

            $scope.viewIndex = $scope.viewIndex + 1;
            if ($scope.viewIndex >= $scope.views.length - 1) {
                $scope.viewIndex = $scope.views.length - 1;
                $scope.backButton.style.backgroundColor = 'grey';
            }
            setView($scope.views[$scope.viewIndex].ponto, $scope.views[$scope.viewIndex].zoom, true);

            if (!$scope.advanceButton && $scope.views.length > 1) {

                var ourCustomControl = L.Control.extend({

                    options: {
                        position: 'topright'
                        //control position - allowed: 'topleft', 'topright', 'bottomleft', 'bottomright'
                    },

                    onAdd: function (map) {
                        var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control leaflet-control-custom');

                        container.style.backgroundColor = 'white';
                        container.style.width = '30px';
                        container.style.height = '30px';
                        container.style.cursor = 'pointer'
                        container.title = $scope.res('AVANCAR_MAPA');
                        container.innerHTML = '<img src="assets/images/right_arrow.svg" style="margin-top:4px; margin-left:4px; width:22px; height:22px;" />';

                        container.onclick = advanceEvent;
                        $scope.advanceButton = container;
                        return container;
                    },

                });

                _obterMapa().then(function (map) {
                    new ourCustomControl().addTo(map);
                });

            }

            if ($scope.advanceButton) {
                $scope.advanceButton.style.backgroundColor = 'white';
            }
        }

       /**
         * Adiciona um cluster para agrupar os Incidentes
         * 
         * @method adicionaCluster
         * 
         * 
         */           
        function adicionaCluster() {
            return _obterMapa().then(function (mapa) {
                mcgPOI.addTo(mapa);
                mcgIncidente.addTo(mapa);
                mcgOthers.addTo(mapa);
            });
        }
        adicionaCluster();

       /**
         * Cria uma camada para agrupar os Pois
         * 
         * @method clearMarkerCluster
         * 
         * 
         */          
        function clearMarkerCluster() {
            mcgPOI.clearLayers();
        }

       /**
         * Cria uma camada para agrupar os Pois, Incidentes e Outros
         * 
         * @method clearAllMarkerCluster
         * 
         * 
         */          
        function clearAllMarkerCluster() {
            mcgPOI.clearLayers();
            mcgOthers.clearLayers();
            mcgIncidente.clearLayers();
        }

      /**
         * Seta a visualização do mapa para uma previamente vista 
         * 
         * @method advanceEvent
         * 
         * @param event {Object}
         */             
        function advanceEvent(event) {

            event.stopPropagation();

            $scope.viewIndex = $scope.viewIndex - 1;

            if ($scope.viewIndex <= 0) {
                $scope.viewIndex = 0;
                $scope.advanceButton.style.backgroundColor = 'grey';
            }
            setView($scope.views[$scope.viewIndex].ponto, $scope.views[$scope.viewIndex].zoom, true);
            $scope.backButton.style.backgroundColor = 'white';

        }

        var map;
        var mcgPOI;
        var mcgOthers; //Cluster para todos os outros conceitos diferentes de incidente e Pois.
        var mcgIncidente;
        inicializaMarkerCluster();

      /**
         * Inicializa os Markers responsáveis pelo agrupamento de Poi, Incidente e outros. 
         * 
         * @method inicializaMarkerCluster
         * 
         */         
        function inicializaMarkerCluster() {
            mcgOthers = L.markerClusterGroup({ maxClusterRadius: 30 });
            mcgPOI = L.markerClusterGroup({ maxClusterRadius: 30 });
            mcgIncidente = L.markerClusterGroup({
                maxClusterRadius: 30,
                iconCreateFunction: function (cluster) {
                    return new L.DivIcon({
                        html: '<div class="marker-cluster-wrapper" ' +
                            'style="width: 24px; height: 24px; background-size: cover; background-image: url(\'' +
                            //(markers[0].urlClusterIcone) + '\');">' +
                            '/assets/images/Incidentes/ic_incident_red_24px.png\');">' +
                            '<b class="marker-cluster-counter ml-3 mt-n2 position-absolute rounded-circle border border-secondary bg-light text-secondary"' +
                            'style="padding-top:2px; text-align:center; width:23px; height:23px;">' +
                            cluster.getChildCount() + '</b></div>',
                        iconSize: [24, 24],
                        iconAnchor: [0, 0]
                    });
                }
            });
        }

      /**
         * Muda a centralização do mapa para as coordenadas passadas por parâmetro
         * 
         * @method voarPara
         * 
         * @param coordinates {Object}
         * 
         */           
        function voarPara(coordinates) {
            _obterMapa().then(function (mapa) {
                mapa.setView({
                    lat: coordinates[1],
                    lng: coordinates[0]
                }, 17, {
                    animation: true
                });
            });
        }

      /**
         * Adiciona uma camada no mapa
         * 
         * @method adicionarLayer
         * 
         * @param layer {Object}
         * 
         */           
        function adicionarLayer(layer) {
            _obterMapa().then(function (mapa) {
                var group = layer.layerGroup;
                group.removeLayer(layer);
                layer.addTo(group);
                layer.layerGroup = group;
            });
        }

      /**
         * Remove do mapa os eventos de zoom
         * 
         * @method clearZoomEvents
         * 
         * @param zoomEvents {Object}
         * 
         */            
        function clearZoomEvents(zoomEvents) {
            _obterMapa().then(function (mapa) {
                for (var index = 0; index < zoomEvents.length; index++) {
                    mapa.off('zoomend', zoomEvents[index].evento);
                }
            });
        }

      /**
         * Cria no mapa um listener de zoom
         * 
         * @method zoomOn
         * 
         * @param name {Object}
         * @param func {Object}
         * 
         */    
        function zoomOn(name, func) {
            _obterMapa().then(function (mapa) {
                mapa.on(name, func);
            });
        }

      /**
         * Remove do mapa o listener de zoom
         * 
         * @method zoomOff
         * 
         * @param name {Object}
         * @param func {Object}
         * 
         */           
        function zoomOff(name, func) {
            _obterMapa().then(function (mapa) {
                mapa.off(name, func);
            });
        }

      /**
         * Ativa a camada de planejamento do mapa
         * 
         * @method ativarCamadaPlanejamento
         * 
         * @param geojson {Object}
         * 
         */         
        function ativarCamadaPlanejamento(geojson) {

            if (typeof geojson == 'string') {
                geojson = JSON.parse(geojson);
            }

            return _obterMapa().then(function (map) {
                var layer = L.geoJson(geojson, {
                    pointToLayer: function (feature, latlng) {

                        var Icon = L.divIcon({
                            html: feature.properties.icon.html,
                            iconSize: [30, 70]
                        });

                        return L.marker(latlng, {
                            icon: Icon
                        });

                    },
                    onEachFeature: function (feature, layer) {
                        if (feature.properties.texto) {
                            layer.popup = new L.Rrose({
                                offset: new L.Point(0, -10),
                                closeButton: false,
                                autoPan: false
                            })
                                .setLatLng(layer.getBounds().getCenter())
                                .setContent(feature.properties.texto);

                            layer.options.texto = feature.properties.texto;

                        }
                        layer.on('mouseover mousemove', function (e) {
                            if (e.target.popup) {
                                e.target.popup
                                    .setLatLng(e.latlng)
                                    .openOn(map);
                            }
                        });
                        layer.on('mouseout', function (e) {
                            map.closePopup()
                        });
                    },
                    style: function (feature) {
                        return feature.style;
                    }
                });

                layer.addTo(map);

                return {
                    feature: geojson,
                    layer: layer
                };
            });
        }

     /**
         * Adiciona uma camada no mapa
         * 
         * @method addLayer
         * 
         * @param layer {Object}
         * 
         */         
        function addLayer(layer) {
            return _obterMapa().then(function (mapa) {
                return mapa.addLayer(layer);
            });
        }

        /**
         * Faz um desenho no mapa, usando o parâmetro recebido
         * 
         * @method draw
         * 
         * @param areaType {Object}
         * 
         */            
        function draw(areaType) {
            return _obterMapa().then(function (mapa) {
                return new L.Draw[areaType](mapa);
            });
        }

        /**
         * Inclui no mapa uma camada de geoJson 
         * 
         * @method adicionarGeoJSON
         * 
         * @param feature {Object}
         * @param options {Object}
         * 
         */         
        function adicionarGeoJSON(feature, options) {
            return _obterMapa().then(function (mapa) {
                var layer = L.geoJson(feature, options);
                layer.addTo(mapa);
                return layer;
            });
        }

        /**
         * Inclui no mapa em uma coordenada específica um label e um valor 
         * 
         * @method highlightFeature
         * 
         * @param e {Object}
         * 
         */         
        function highlightFeature(e) {
            _obterMapa().then(function (mapa) {
                var popup = L.popup()
                    .setLatLng(e.latlng)
                    .setContent('<h4><b>' + e.target.feature.properties.label + ' = ' + e.target.feature.properties.valor + "</b></h4>")
                    .openOn(map);
            })
        }

        /**
         * Inclui um Marker no mapa 
         * 
         * @method addMarker
         * 
         * @param marker {Object}
         * 
         */             
        function addMarker(marker) {
            _obterMapa().then(function (mapa) {
                marker.addTo(mapa);
            })
        }

        /**
         * Adiciona uma geometria no mapa
         * 
         * @method adicionarGeometria
         * 
         * @param feature {Object}
         * @param tipo {Object}
         * @param layerGroup {Object}
         * 
         */         
        function adicionarGeometria(feature, tipo, layerGroup) {

            var layer = _obterMapa()
                .then(function (mapa) {

                    var camada = _obterPonto(feature, tipo);

                    var aggregate = L.layerGroup();
                    aggregate.addLayer(camada.layer);
                    camada.layer.layerGroup = aggregate;
                    layerGroup.addLayer(camada.layer);

                    aggregate.addTo(mapa);

                    return {
                        aggregate: aggregate,
                        camadas: camada
                    };

                });

            return layer;
        }

        /**
         * Remove uma camada do mapa
         * 
         * @method removerCamada
         * 
         * @param layer {Object}
         * @param layerGroup {Object}
         * 
         */          
        function removerCamada(layer, layerGroup) {
            return _obterMapa().then(function (mapa) {

                var superLayer = layerGroup || mapa;
                if (layer != null) {
                    superLayer.removeLayer(layer);
                }
            });
        }

       /**
         * Verifica se a camada está no mapa
         * 
         * @method possuiLayer
         * 
         * @param layer {Object}
         * 
         */           
        function possuiLayer(layer) {
            return _obterMapa().then(function (mapa) {
                return mapa.hasLayer(layer);
            })
        }

       /**
         * Verifica se a camada está no mapa
         * 
         * @method adicionarPonto
         * 
         * @param feature {Object}
         * @param tipo {Object}
         * @param layerGroup {Object}
         * 
         */                
        function adicionarPonto(feature, tipo, layerGroup) {

            var layer = _obterMapa().then(function (mapa) {

                var camada = _obterPonto(feature, tipo);

                var layerGroup = L.layerGroup();
                layerGroup.addLayer(camada.layer);
                camada.layer.layerGroup = layerGroup;

                //adicionar layer ao mapa somente se ele não já fizer parte de um layer group
                // fix do cluster de viaturas e viaturas perdidas no mapa
                //camada.layer.addTo(mapa);
                layerGroup.addTo(mapa);

                //return camada;

                return {
                    aggregate: layerGroup,
                    camadas: camada
                };

            });

            return layer;
        }

        /**
         * Obter label
         * 
         * @method _obterLabel
         * 
         * @param tipo {Object}
         * @param feature {Object}
         * 
         */          
        function _obterLabel(tipo, feature) {
            return labels[tipo](feature);
        }

        /**
         * Cria um marker para cada feature recebida como parâmetro
         * 
         * @method onEachFeature
         * 
         * @param feature {Object}
         * @param layer {Object}
         * 
         */           
        function onEachFeature(feature, layer) {

            if (!feature.properties.latitude || !feature.properties.longitude) {
                return;
            }

            var sede = [];
            sede.push(feature.properties.latitude);
            sede.push(feature.properties.longitude);

            var marker = L.marker(sede, {
                icon: L.icon({
                    iconUrl: feature.style.iconSede,
                    iconSize: [24, 24]
                })
            });

            feature.marker = marker;

        }

        /**
         * Obtem ponto de interesse, a partir de uma feature
         * 
         * @method _obterPonto
         * 
         * @param feature {Object}
         * @param tipo {Object}
         * 
         */              
        function _obterPonto(feature, tipo) {
            // Não é um ponto
            if (feature.geometry !== null && feature.geometry.type && feature.geometry.type !== 'Point') {

                var geoJson = L.geoJson(feature, {
                    style: feature.style,
                    onEachFeature: onEachFeature
                });

                //Se tem layer da sede da geometria, adiciona ela dentro do layer do Poi.
                if (feature.marker) {
                    geoJson.addLayer(feature.marker);
                }

                if (feature.properties.nome || feature.properties.label) {
                    geoJson.bindLabel(feature.properties.nome || feature.properties.label);
                }

                if (geoJson.getLayers()[0]._latlngs) {
                    var latlng = geoJson.getLayers()[0]._latlngs[0];
                    geoJson.on('click', click['polygon'](feature.properties.id, latlng, feature.properties.tipo));
                } else {
                    geoJson.on('click', click['polygon'](feature.properties.id, feature.properties.tipo));
                }

                return {
                    feature: feature,
                    layer: geoJson
                };
            }

            var latLng;

            if (feature.geometry.coordinates) {
                latLng = {
                    lat: feature.geometry.coordinates[1],
                    lng: feature.geometry.coordinates[0]
                };
            } else if (feature.properties != null && feature.properties.geometry != null && feature.properties.geometry.coordinates != null) {
                latLng = {
                    lat: feature.properties.geometry.coordinates[0][1],
                    lng: feature.properties.geometry.coordinates[0][0]
                };
            }
            if (latLng != null && latLng.lat != null && latLng.lng != null) {

                var html = MapaService.obterHtmlIcon(feature, tipo);
                var w = 24, h = 24;
                if (feature.representacao) {
                    w = feature.representacao.iconAnchor.w;
                    h = feature.representacao.iconAnchor.h;
                }

                var marker = L.Marker.movingMarker([latLng], 1500, {
                    icon: new L.DivIcon({
                        html: html,
                        iconSize: [w, h]
                    })
                });

                marker.bindLabel(_obterLabel(tipo, feature));

                marker.on('click', click[tipo](feature.properties._id || feature.properties.id || feature.properties));
                if (tipo == 'camera') {
                    if ($rootScope.ParametrosS4C.multivisConfig
                        && $rootScope.ParametrosS4C.multivisConfig.name
                        && $rootScope.ParametrosS4C.multivisConfig.hostname
                        && $rootScope.ParametrosS4C.multivisConfig.key) {
                        marker.bindContextMenu({
                            contextmenu: true,
                            contextmenuItems: [{
                                text: localize.res('OPEN_MULTIVIS', $rootScope.ParametrosS4C.multivisConfig.name),
                                hideOnSelect: true,
                                callback: function (e) {
                                    Camera.obterPorId(feature.properties.id).then(function (camera) {
                                        _openCameraMultivis(camera.url);
                                    });
                                }
                            }],
                            contextmenuInheritItems: true
                        });
                    }
                }

                if (tipo == 'poi') {
                    marker.bindContextMenu({
                        contextmenu: true,
                        contextmenuItems: [{
                            text: $scope.res('CRIAR_INCIDENTE'),
                            hideOnSelect: true,
                            callback: function (e) {
                                IncidenteEnterManager.abrirAlfinete(marker, feature.properties.chave_estrangeira);
                            }
                        }],
                        contextmenuInheritItems: true
                    });
                }

                return {
                    feature: feature,
                    layer: marker
                };
            }
        }

        var layerList = [];
        /**
         * Adiciona Pontos na Feature Collection 
         * 
         * @method adicionarPontos
         * 
         * @param featureCollection {Object}
         * @param zoom {Object}
         * 
         */           
        function adicionarPontos(featureCollection, zoom) {
            return _obterMapa().then(function (mapa) {

                if (!featureCollection) {
                    return;
                }

                if (!zoom) {
                    var sameValue = undefined;
                    sameValue = _.find(layerList, function (layer) {
                        return (JSON.stringify(layer) === JSON.stringify(featureCollection.features));
                    })
                    if (sameValue) return;
                    layerList.push(angular.copy(featureCollection.features));
                }

                map = mapa;
                //Verifico se a instância do mapa mudou (preset). Caso tenha mudado atualizo o cluster.
                if (mcgPOI._map._leaflet_id != map._leaflet_id || mcgIncidente._map._leaflet_id != map._leaflet_id) {
                    inicializaMarkerCluster();
                    mcgPOI.addTo(map);
                    mcgIncidente.addTo(map);
                    mcgOthers.addTo(map);
                }

                var camadas = _.map(_.filter(featureCollection.features, function (f) {
                    return f.geometry != undefined
                }), function (feature) {
                    if (feature.geometry) {
                        return _obterPonto(feature, featureCollection.properties.tipo);
                    }
                });


                if (featureCollection.properties != null && (featureCollection.properties.cluster == null || featureCollection.properties.cluster.id != 3)) {

                    var clusterDistance = parseInt(ParametrosS4C.parametros.cluster.pois.distancia, 10) / 10;
                    var group;

                    if (featureCollection.properties.tipo === 'incidente') {
                        clusterDistance = 50;
                        group = L.featureGroup.subGroup(mcgIncidente);
                    }
                    else if (featureCollection.representacao) {
                        group = L.featureGroup.subGroup(mcgPOI);
                    } else {
                        group = L.featureGroup.subGroup(mcgOthers);
                    }

                    if (clusterDistance <= 0) {
                        clusterDistance = 1;
                    }

                    var planta = false;

                    _.each(camadas, function (camada) {
                        if (camada != null && camada.feature.properties != null && camada.feature.properties.chave_estrangeira != null
                            && camada.feature.properties.chave_estrangeira.includes("planta_riba")) {

                            var poly = L.geoJson(camada.feature, { weight: 1 });
                            markerCluster.addLayer(poly);
                            planta = true;

                        } else if (camada != null) {

                            if (!camada.layer) {
                                camada.getLayers()[0].urlClusterIcone = featureCollection.properties.urlClusterIcone;
                                camada.getLayers()[0].AcervoId = camada.feature.properties.AcervoId;
                                camada.getLayers()[0].addTo(group);
                                camada.getLayers()[0].layerGroup = group;
                            } else {
                                if (featureCollection.properties.tipo === 'poi' && (featureCollection.properties.urlClusterIcone == null || featureCollection.properties.urlClusterIcone == '')) {
                                    camada.layer.urlClusterIcone = "/assets/images/generic_poi_icon.svg";
                                } else {
                                    camada.layer.urlClusterIcone = featureCollection.properties.urlClusterIcone;
                                }
                                camada.layer.AcervoId = camada.feature.properties.AcervoId;
                                camada.layer.addTo(group);
                                camada.layer.layerGroup = group;
                            }
                        }

                    });

                    group.addTo(map);

                    return {
                        aggregate: group,
                        camadas: camadas
                    };

                } else {

                    var layerGroup = L.layerGroup();

                    _.each(camadas, function (camada) {
                        if (camada != null && camada.feature.properties != null && camada.feature.properties.chave_estrangeira != null
                            && camada.feature.properties.chave_estrangeira.includes("planta_riba")) {

                            var poly = L.geoJson(camada.feature, { weight: 1 });
                            mapa.addLayer(poly);

                        } else if (camada != null) {
                            if (!camada.layer) {
                                camada.getLayers()[0].AcervoId = camada.feature.properties.AcervoId;
                                layerGroup.AcervoId = camada.feature.properties.AcervoId;
                                layerGroup.addLayer(camada.getLayers()[0]);
                                camada.getLayers()[0].layerGroup = layerGroup;
                            } else {
                                camada.layer.AcervoId = camada.feature.properties.AcervoId;
                                layerGroup.AcervoId = camada.feature.properties.AcervoId;
                                layerGroup.addLayer(camada.layer);
                                camada.layer.layerGroup = layerGroup;
                            }
                        }
                        layerGroup.addTo(mapa);
                    });

                    return {
                        aggregate: layerGroup,
                        camadas: camadas
                    };
                }
            })
        }

        /**
         * Obtem ponto central 
         * 
         * @method obterPontoCentral
         * 
         * 
         */          
        function obterPontoCentral() {
            return _obterMapa().then(function (mapa) {
                return mapa.getCenter();
            });
        }

        /**
         * Ajusta o Mapa para caber os tweets
         * 
         * @method fitBounds
         * 
         * @param coordinates {Object}
         * @param tweets {Object}
         * 
         */            
        function fitBounds(coordinates, tweets) {
            _obterMapa().then(function (mapa) {

                if (tweets) {
                    mapa.adicionarTweets(tweets);
                }
                mapa.fitBounds(coordinates);
            });
        }

        /**
         * Atualiza as informações da camada
         * 
         * @method updateClick
         * 
         * @param tipo {Object}
         * @param camada {Object}
         * 
         */         
        function updateClick(tipo, camada) {
            camada.layer.removeEventListener('click');
            camada.layer.addEventListener('click', click[tipo](camada.feature.properties._id || camada.feature.properties.id || camada.feature.properties));
        }

        angular.extend($scope, {
            controls: {},
            defaults: {
            },
            layers: {
                baselayers: {
                    mapbox: {
                        name: $scope.res('MAPBOX_DIA'),
                        type: 'xyz',
                        url: 'https://api.mapbox.com/styles/v1/adrianogoncalves/cjfofver42ym12spaov93wo1w/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoiYWRyaWFub2dvbmNhbHZlcyIsImEiOiJjaXh2ejZjMDcwMDE1MndyeTZ3anJwY25tIn0.KmLdvo0iZnhjW4X86uPplg',
                        top: true
                    },
                    night: {
                        name: $scope.res('MAPBOX_NOITE'),
                        type: 'xyz',
                        url: 'https://api.mapbox.com/styles/v1/adrianogoncalves/cjgzfbrip00072ro65ix8lkzo/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoiYWRyaWFub2dvbmNhbHZlcyIsImEiOiJjaXh2ejZjMDcwMDE1MndyeTZ3anJwY25tIn0.KmLdvo0iZnhjW4X86uPplg'
                    },
                    mapboxSatelite: {
                        name: $scope.res('MAPBOX_SATELITE'),
                        type: 'xyz',
                        url: 'https://api.mapbox.com/styles/v1/adrianogoncalves/cixvzhep2000i2sp6eyqz2jh8/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoiYWRyaWFub2dvbmNhbHZlcyIsImEiOiJjaXh2ejZjMDcwMDE1MndyeTZ3anJwY25tIn0.KmLdvo0iZnhjW4X86uPplg'

                    },
                    osm: {
                        name: 'Open Street Maps',
                        type: 'xyz',
                        url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
                        layerOptions: {
                            subdomains: ['a', 'b', 'c'],
                            attribution: '© OpenStreetMap contributors',
                            continuousWorld: true
                        }
                    }
                }
            },
            events: {
                map: {
                    enable: [
                        'moveend',
                        'mouseover'
                    ],
                    logic: 'emit'
                },
                marker: {
                    enable: [],
                    logic: 'emit'
                }
            },
            center: {
                lat: coordsIniciais.latitude,
                lng: coordsIniciais.longitude,
                zoom: coordsIniciais.zoom
            },
            markers: [],
            baseConhecimentoSelecionada: {
                ativo: false,
                obj: null,
                time: null
            },
            cameraSelecionada: {
                ativo: false,
                obj: null
            },
            mapaPrincipal: mapaPrincipal,
            $apiMapa: {
                adicionaCluster: adicionaCluster,
                updateClick: updateClick,
                draw: draw,
                zoomOff: zoomOff,
                zoomOn: zoomOn,
                clearZoomEvents: clearZoomEvents,
                addMarker: addMarker,
                addLayer: addLayer,
                highlightFeature: highlightFeature,
                setView: setView,
                fitBounds: fitBounds,
                obterPontoCentral: obterPontoCentral,
                voarPara: voarPara,
                adicionarPontos: adicionarPontos,
                adicionarPonto: adicionarPonto,
                possuiLayer: possuiLayer,
                removerCamada: removerCamada,
                adicionarGeometria: adicionarGeometria,
                adicionarGeoJSON: adicionarGeoJSON,
                ativarCamadaPlanejamento: ativarCamadaPlanejamento,
                adicionarLayer: adicionarLayer,
                inicializaMarkerCluster: inicializaMarkerCluster,
                clearMarkerCluster: clearMarkerCluster,
                clearAllMarkerCluster: clearAllMarkerCluster,
                obterMapa: _obterMapa,
                obterRotaAtiva: obterRotaAtiva,
                obterRotaUnificadaAtiva: obterRotaUnificadaAtiva,
                /* Categorias */
                poiAtivo: $scope.poiAtivo,
                pingLayer: $scope.pingLayer,
                /* Planejamento */
                ativarCamada: ativarCamada,
                desativarCamada: desativarCamada,
                /* Rotas */
                desenharPoligono: desenharPoligono,
                desenharPoligonoRotaUnficada: desenharPoligonoRotaUnficada,
                /* Util */
                checarCamadaAtiva: checarCamadaAtiva,
                piscarVermelho: piscarVermelho,
                naoPiscar: naoPiscar,
                piscarAzul: piscarAzul,
                blueFitBounds: blueFitBounds,
                removerPoiClicado: removerPoiClicado,
                removerLinhas: removerLinhas,
                dataminingViewport: dataminingViewport,
                removerDesenho: removerDesenho,
                colocarIcon: colocarIcon,
                gridResized: gridResized,
                desenharRota: desenharRota,
                desenharRotaUnificada: desenharRotaUnificada,
                desenharRotaUnificadaKml: desenharRotaUnificadaKml,
                desenharTrajetoRotaUnificadaKml: desenharTrajetoRotaUnificadaKml,
                removerPonto: removerPonto,
                removerPontoRotaUnificada: removerPontoRotaUnificada,
                removerRouter: removerRouter,
                removerRouterRotaUnificada: removerRouterRotaUnificada,
                removerRotasUnificadasDoMapa: removerRotasUnificadasDoMapa,
                removerZonaObservacao: removerZonaObservacao,
                removerPontosPlanejamento: removerPontosPlanejamento,
                pegarZoom: pegarZoom,
                removerRotasDesenhadas: removerRotasDesenhadas,
                salvarRota: salvarRota,
                salvarRotaUnificada: salvarRotaUnificada,
                planejar: planejar,
                pegarGeometrias: pegarGeometrias,
                removerControleDesenho: removerControleDesenho,
                flyTo: flyTo,
                desenharGeometria: desenharGeometria,
                drawLine: drawLine,
                clearLines: clearLines,
                blueFlyTo: blueFlyTo,
                marcarBbox: marcarBbox,
                ativarKml: ativarKml,
                ativarShapeFile: ativarShapeFile,
                fecharEditor: function () {
                    $scope.router.setWaypoints([]);
                },
                pegarBounds: function () {
                    var deferred = $q.defer();

                    _obterMapa()
                        .then(function (map) {
                            deferred.resolve(map.getBounds());
                        });

                    return deferred.promise;
                },
                showCameras: showCameras,
                drawCameraLines: drawCameraLines,
                drawPoiLines: drawPoiLines,
                drawIncidentesLines: drawIncidentesLines,
                drawBasesLines: drawBasesLines,
                changeClass: changeClass,
                adicionarSubItem: adicionarSubItem,
                removerLayers: removerLayers,
                adicionarTweets: adicionarTweets,
                limparTweetsAtivos: limparTweetsAtivos,
                twitterFlyTo: twitterFlyTo,
                limparSubItem: limparSubItem,
                fitBounds: fitBounds,
                resetarDetalhamento: resetarDetalhamento,
                adicionarLinha: adicionarLinha,
                desenharRaioTwitter: desenharRaioTwitter,
                removerRaioTwitter: removerRaioTwitter,
                definirCorDaLinha: definirCorDaLinha,
                desenharFromJSON: desenharFromJSON,
                removerDesenhosZonaObservacao: removerDesenhosZonaObservacao,
                pegarPontosDaRota: pegarPontosDaRota,
                pegarPontosDaRotaUnificada: pegarPontosDaRotaUnificada,
                PontoInfo: PontoInfo,
                removerDesenhoPlanejamento: removerDesenhoPlanejamento,
                desenharRegioes: desenharRegioes,
                destacarSubItem: destacarSubItem,
                desenharPontoMovel: desenharPontoMovel,
                desenharERs: desenharERs,
                desenharPercursoER: desenharPercursoER,
                atualizarPosicaoPontoMovel: atualizarPosicaoPontoMovel,
                setView: setView,
                desenharCirculosPois: desenharCirculosPois,
                removerCirculosPois: removerCirculosPois,
                removeLayer: removeLayer,
                enquadrarPoisRelacionados: enquadrarPoisRelacionados,
                refreshMap: refreshMap
            },
            possuiIcones: possuiIcones,
            invalidate: invalidate,
            dataminingViewport: dataminingViewport,
            salvarRota: salvarRota,
            salvarRotaUnificada: salvarRotaUnificada,
            removerControleDesenho: removerControleDesenho,
            showCameras: showCameras,
            drawCameraLines: drawCameraLines,
            orderCameras: orderCameras,
            listBack: listBack,
            listForward: listForward,
            abrirLegenda: abrirLegenda,
            IncidenteEnterManager: IncidenteEnterManager,
            ZonaDeObservacaoManager: ZonaDeObservacaoManager,
            RastreamentoManager: RastreamentoManager,
            PontoMovelManager: PontoMovelManager,
            removeLayer: removeLayer,
            MapaManager: MapaManager,
            blackList: $scope.blackList

        });

        MapaService.addMap($scope.$apiMapa);
        $scope.$on('$destroy', function () {
            MapaService.removeMap($scope.$apiMapa);
        });
    }

    angular.module('s4c.components.mapa', ['angular-toasty', 's4c.components.incidentes'])
        .directive('s4cMapa', s4cMapa)
        .directive('fallbackSrc', function () {
            return {
                link: function (scope, $elem, $attrs) {
                    $elem.bind('error', function (e) {
                        angular.element(this)
                            .attr('src', $attrs.fallbackSrc);

                        e.target.removeEventListener(e.type);
                    });
                }
            };
        });
}());