Source: directives/mapa-area-de-atuacao/mapa-area-de-atuacao.js

/**
 * @ngdoc directive
 * @name MapaAreaDeAtuacao
 * @module s4c.directives.MapaAreaDeAtuacao
 *
 * @description
 * `MapaAreaDeAtuacaoCtrl` Controller do módulo do mapa da área de atuação
 * 
 * @example <mapa-area-de-atuacao style="width: 55%; height: 500px;"></mapa-area-de-atuacao>
 * 
 */
(function () {
    'use strict';

    angular.module('s4c.directives.mapa-area-de-atuacao', [])
        .directive('mapaAreaDeAtuacao', MapaAreaDeAtuacao);

    MapaAreaDeAtuacaoCtrl.$inject = ['$scope', 'leafletData', 'MapaAreaDeAtuacao', 'PlanejamentoDesenhoService', 'MainState', '$mdDialog'];

    function MapaAreaDeAtuacaoCtrl($scope, leafletData, MapaAreaDeAtuacao, PlanejamentoDesenhoService, MainState, $mdDialog) {

        $scope.center = {
            lat: -22.9009167,
            lng: -43.1795591,
            zoom: 13
        };

        /**
         * Criação das funções do módulo do mapa da area de atuação
         * 
         * @method buildMap
         * 
         * 
         * @param callback {Function}
         * @param areaAtuacao {Object}
         * 
         */         
        function buildMap(callBack, areaAtuacao) {

            $scope.areaAtuacao = areaAtuacao;

            leafletData
                .getMap('mapa-area-de-atuacao')
                .then(function (map) {

	                /*map.on('draw:created', function(e) {
	
	                    map.addLayer(e.layer);
	                    e.layer.on('edit', function () {
	                        MapaAreaDeAtuacao.trigger('areaDeAtuacaoEditada');
	                    });
	                    e.layer.editing.enable();
	                    MapaAreaDeAtuacao._layer = e.layer;
	                    MapaAreaDeAtuacao.trigger('areaDeAtuacaoEditada');
	                })*/

                    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)

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

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

                        map.addLayer($scope.drawnItems);
                        map.options.allowIntersection = false;

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

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

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

                                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
                                }
                            },
                            edit: {
                                edit: false,
                                remove: true,
                                featureGroup: $scope.drawnItems
                            }
                        });

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

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

                        map.on('draw:drawstart', function (e) {
                            if (!MapaAreaDeAtuacao._regiao) {
                                map.fire('draw:cancel', { handler: e.type });
                                return;
                            }
                            var offsetX = $scope.drawControl._container.offsetLeft;
                            var offsetY = $scope.drawControl._container.offsetTop;
                        });

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

                    map.off('draw:created', drawCreated);
                    map.on('draw:created', drawCreated);

                    MapaAreaDeAtuacao._map = map;
                    callBack();
                });
        }

     /**
         * Criação das funções de desenho do mapa da area de atuação
         * 
         * @method drawCreated
         * 
         * 
         * @param e {event}
         * 
         */         
        function drawCreated(e) {

            if (!MapaAreaDeAtuacao._regiao) {
                MapaAreaDeAtuacao._map.fire('draw:cancel', { handler: e.type });
                $mdDialog
                    .show($mdDialog.alert()
                        .title($scope.res('COMUM_ERRO'))
                        .content($scope.res('REGIAO_SELECIONAR'))
                        .ok($scope.res('COMUM_OK')));
                return;
            }

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

            var intersection = false;

            _.each(MapaAreaDeAtuacao._array_layer, function (layer_inserted) {
                var checked = crossCheck(layer_inserted, layer);
                if (checked.geometries.length > 0) {
                    intersection = true;
                }
            	/*if( layer.getBounds().intersects(layer_inserted.getBounds()) ){
            		intersection = true;
            	}*/
            });

            if (intersection) {
                MapaAreaDeAtuacao._map.fire('draw:cancel', { handler: e.type });
                $mdDialog
                    .show($mdDialog.alert()
                        .title($scope.res('COMUM_ERRO'))
                        .content($scope.res('REGIAO_INTERSECAO'))
                        .ok($scope.res('COMUM_OK')));
                return;
            }

            layer.editing.disable();

            layer.setStyle({
                weight: 5,
                color: $scope.areaAtuacao.cor,
                dashArray: '',
                fillOpacity: 0.8
            });

            $scope.drawnItems.addLayer(layer);
            MapaAreaDeAtuacao._layer = layer;
            layer.data = MapaAreaDeAtuacao._regiao;
            MapaAreaDeAtuacao._array_layer.push(layer);
            MapaAreaDeAtuacao.trigger('areaDeAtuacaoEditada');
        }

        /**
         * Retorna a Área que faz interseção entre 2 lines string
         * 
         * @method lineStringsIntersect
         * 
         * 
         * @param l1 {Object}
         * @param l2 {Object}
         * 
         * @returns Intersects 
         * 
         */           
        function lineStringsIntersect(l1, l2) {
            var intersects = [];
            for (var i = 0; i <= l1.coordinates.length - 2; ++i) {
                for (var j = 0; j <= l2.coordinates.length - 2; ++j) {
                    var a1Latlon = L.latLng(l1.coordinates[i][1], l1.coordinates[i][0]),
                        a2Latlon = L.latLng(l1.coordinates[i + 1][1], l1.coordinates[i + 1][0]),
                        b1Latlon = L.latLng(l2.coordinates[j][1], l2.coordinates[j][0]),
                        b2Latlon = L.latLng(l2.coordinates[j + 1][1], l2.coordinates[j + 1][0]),
                        a1 = L.Projection.SphericalMercator.project(a1Latlon),
                        a2 = L.Projection.SphericalMercator.project(a2Latlon),
                        b1 = L.Projection.SphericalMercator.project(b1Latlon),
                        b2 = L.Projection.SphericalMercator.project(b2Latlon),
                        ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),
                        ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),
                        u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);
                    if (u_b != 0) {
                        var ua = ua_t / u_b,
                            ub = ub_t / u_b;
                        if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
                            var pt_x = a1.x + ua * (a2.x - a1.x),
                                pt_y = a1.y + ua * (a2.y - a1.y),
                                pt_xy = {
                                    "x": pt_x,
                                    "y": pt_y
                                },
                                pt_latlon = L.Projection.SphericalMercator.unproject(pt_xy);
                            intersects.push({
                                'type': 'Point',
                                'coordinates': [pt_latlon.lng, pt_latlon.lat]
                            });
                        }
                    }
                }
            }
            if (intersects.length == 0) intersects = false;
            return intersects;
        }

        //takes GeoJSON as input, creates a GeoJSON GeometryCollection of linestrings as output

        /**
         * Takes GeoJSON as input, creates a GeoJSON GeometryCollection of linestrings as output
         * 
         * @method lineify
         * 
         * 
         * @param inputGeom {Object}
         * 
         * @returns GeometryCollection of linestrings
         */           
        function lineify(inputGeom) {
            var outputLines = {
                "type": "GeometryCollection",
                "geometries": []
            }
            switch (inputGeom.type) {
                case "GeometryCollection":
                    for (var i in inputGeom.geometries) {
                        var geomLines = lineify(inputGeom.geometries[i]);
                        if (geomLines) {
                            for (var j in geomLines.geometries) {
                                outputLines.geometries.push(geomLines.geometries[j]);
                            }
                        } else {
                            outputLines = false;
                        }
                    }
                    break;
                case "Feature":
                    var geomLines = lineify(inputGeom.geometry);
                    if (geomLines) {
                        for (var j in geomLines.geometries) {
                            outputLines.geometries.push(geomLines.geometries[j]);
                        }
                    } else {
                        outputLines = false;
                    }
                    break;
                case "FeatureCollection":
                    for (var i in inputGeom.features) {
                        var geomLines = lineify(inputGeom.features[i].geometry);
                        if (geomLines) {
                            for (var j in geomLines.geometries) {
                                outputLines.geometries.push(geomLines.geometries[j]);
                            }
                        } else {
                            outputLines = false;
                        }
                    }
                    break;
                case "LineString":
                    outputLines.geometries.push(inputGeom);
                    break;
                case "MultiLineString":
                case "Polygon":
                    for (var i in inputGeom.coordinates) {
                        outputLines.geometries.push({
                            "type": "LineString",
                            "coordinates": inputGeom.coordinates[i]
                        });
                    }
                    break;
                case "MultiPolygon":
                    for (var i in inputGeom.coordinates) {
                        for (var j in inputGeom.coordinates[i]) {
                            outputLines.geometries.push({
                                "type": "LineString",
                                "coordinates": inputGeom.coordinates[i][j]
                            });
                        }
                    }
                    break;
                default:
                    outputLines = false;
            }
            return outputLines;
        }

        //takes Leaflet layers as input; produces geoJSON points of their intersections as output
        /**
         * Takes Leaflet layers as input; produces geoJSON points of their intersections as output
         * 
         * @method crossCheck
         * 
         * 
         * @param baseLayer {Object}
         * @param drawLayer {Object}
         * 
         * @returns Gera um geojson com os pontos de interseção
         */           
        function crossCheck(baseLayer, drawLayer) {
            var baseJson = baseLayer.toGeoJSON(),
                drawJson = drawLayer.toGeoJSON(),
                baseLines = lineify(baseJson),
                drawLines = lineify(drawJson),
                crossPoints = {
                    type: "GeometryCollection",
                    geometries: []
                };
            if (baseLines && drawLines) {
                for (var i in drawLines.geometries) {
                    for (var j in baseLines.geometries) {
                        var crossTest = lineStringsIntersect(drawLines.geometries[i], baseLines.geometries[j]);
                        if (crossTest) {
                            for (var k in crossTest) {
                                crossPoints.geometries.push(crossTest[k]);
                            }
                        }
                    }
                }
            }
            return crossPoints;
        }


        angular.extend($scope, {
            $api: {
                buildMap: buildMap
            }
        });

        MainState.registerDirective('mapaAreaDeAtuacao', $scope.$api);
        $scope.$on('$destroy', function () {
            MainState.unregisterDirective('mapaAreaDeAtuacao');
        });
    }

    function MapaAreaDeAtuacao() {
        return {
            $scope: {
                desenhar: '@'
            },
            templateUrl: 'app/directives/mapa-area-de-atuacao/mapa-area-de-atuacao.html',
            controller: MapaAreaDeAtuacaoCtrl
        }
    };

}());