Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angularjs with Leafletjs

Following directie code is from http://jsfiddle.net/M6RPn/26/ I want to get a json feed that has many lat and long.. I can get a json with $resource or $http in Angular easily but How can I feed it to this directive to map thing on the map?

module.directive('sap', function() {
    return {
        restrict: 'E',
        replace: true,
        template: '<div></div>',
        link: function(scope, element, attrs) {
            var map = L.map(attrs.id, {
                center: [40, -86],
                zoom: 10
            });
            //create a CloudMade tile layer and add it to the map
            L.tileLayer('http://{s}.tile.cloudmade.com/57cbb6ca8cac418dbb1a402586df4528/997/256/{z}/{x}/{y}.png', {
                maxZoom: 18
            }).addTo(map);

            //add markers dynamically
            var points = [{lat: 40, lng: -86},{lat: 40.1, lng: -86.2}];
            for (var p in points) {
                L.marker([points[p].lat, points[p].lng]).addTo(map);
            }
        }
    };
});
like image 399
MomentH Avatar asked Oct 19 '12 03:10

MomentH


4 Answers

I don't know a lot about Leaflet or what you're trying to do, but I'd assume you want to pass some coordinates in from your controller to your directive?

There are actually a lot of ways to do that... the best of which involve leveraging scope.

Here's one way to pass data from your controller to your directive:

module.directive('sap', function() {
    return {
        restrict: 'E',
        replace: true,
        template: '<div></div>',
        link: function(scope, element, attrs) {
            var map = L.map(attrs.id, {
                center: [40, -86],
                zoom: 10
            });
            //create a CloudMade tile layer and add it to the map
            L.tileLayer('http://{s}.tile.cloudmade.com/57cbb6ca8cac418dbb1a402586df4528/997/256/{z}/{x}/{y}.png', {
                maxZoom: 18
            }).addTo(map);

            //add markers dynamically
            var points = [{lat: 40, lng: -86},{lat: 40.1, lng: -86.2}];
            updatePoints(points);

            function updatePoints(pts) {
               for (var p in pts) {
                  L.marker([pts[p].lat, pts[p].lng]).addTo(map);
               }
            }

            //add a watch on the scope to update your points.
            // whatever scope property that is passed into
            // the poinsource="" attribute will now update the points
            scope.$watch(attr.pointsource, function(value) {
               updatePoints(value);
            });
        }
    };
});

Here's the markup. In here you're adding that pointsource attribute the link function is looking for to set up the $watch.

<div ng-app="leafletMap">
    <div ng-controller="MapCtrl">
        <sap id="map" pointsource="pointsFromController"></sap>
    </div>
</div>

Then in your controller you have a property you can just update.

function MapCtrl($scope, $http) {
   //here's the property you can just update.
   $scope.pointsFromController = [{lat: 40, lng: -86},{lat: 40.1, lng: -86.2}];

   //here's some contrived controller method to demo updating the property.
   $scope.getPointsFromSomewhere = function() {
     $http.get('/Get/Points/From/Somewhere').success(function(somepoints) {
         $scope.pointsFromController = somepoints;
     });
   }
}
like image 137
Ben Lesh Avatar answered Nov 20 '22 18:11

Ben Lesh


I recently built an app using Angular JS and Leaflet. Very similar to what you've described, including location data from a JSON file. My solution is similar to blesh.

Here's the basic process.

I have a <map> element on one of my pages. I then have a Directive to replace the <map> element with the Leaflet map. My setup is slightly different because I load the JSON data in a Factory, but I've adapted it for your use case (apologies if there are errors). Within the Directive, load your JSON file, then loop through each of your locations (you'll need to setup your JSON file in a compatible way). Then display a marker at each lat/lng.

HTML

<map id="map" style="width:100%; height:100%; position:absolute;"></map>

Directive

app.directive('map', function() {
return {
    restrict: 'E',
    replace: true,
    template: '<div></div>',
    link: function(scope, element, attrs) {

        var popup = L.popup();
        var southWest = new L.LatLng(40.60092,-74.173508);
        var northEast = new L.LatLng(40.874843,-73.825035);            
        var bounds = new L.LatLngBounds(southWest, northEast);
        L.Icon.Default.imagePath = './img';

        var map = L.map('map', {
            center: new L.LatLng(40.73547,-73.987856),
            zoom: 12,
            maxBounds: bounds,
            maxZoom: 18,
            minZoom: 12
        });



        // create the tile layer with correct attribution
        var tilesURL='http://tile.stamen.com/terrain/{z}/{x}/{y}.png';
        var tilesAttrib='Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.';
        var tiles = new L.TileLayer(tilesURL, {
            attribution: tilesAttrib, 
            opacity: 0.7,
            detectRetina: true,
            unloadInvisibleTiles: true,
            updateWhenIdle: true,
            reuseTiles: true
        });
        tiles.addTo(map);

        // Read in the Location/Events file 
        $http.get('locations.json').success(function(data) {
            // Loop through the 'locations' and place markers on the map
            angular.forEach(data.locations, function(location, key){

                var marker = L.marker([location.latitude, location.longitude]).addTo(map);

            });
        });
    }
};

Sample JSON File

{"locations": [     
{   
    "latitude":40.740234, 
    "longitude":-73.995715
    }, 
{   
    "latitude":40.74277, 
    "longitude":-73.986654
    },
{   
    "latitude":40.724592, 
    "longitude":-73.999679
    }
]} 
like image 41
Brett DeWoody Avatar answered Nov 20 '22 18:11

Brett DeWoody


Directives and mvc in angularJs are different technologies. Directives are usually executed when the page loads. Directives are more for working on/with html and xml. Once you have JSON, then its best to use the mvc framework to do work.

After the page has rendered, to apply directives you often need to do $scope.$apply() or $compile to register the change on the page.

Either way, the best way to get a service into a directive is by using the dependency injection framework.

I noticed the scope:true, or scope:{} was missing from your directive. This has a big impact on how well the directive plays with parent controllers.

app.directive('mapThingy',['mapSvc',function(mapSvc){
  //directive code here.

}]);

app.service('mapSvc',['$http',function($http){
 //svc work here.
}])

Directives are applied by camelCase matching. I would avoid using or because of an issue with IE. Alternative would be

<div map-thingy=""></div>
like image 32
Eric Rohlfs Avatar answered Nov 20 '22 18:11

Eric Rohlfs


Assuming that in your controller you got

$scope.points = // here goes your retrieved data from json

and your directive template is:

<sap id="nice-map" points="points"/>

then inside your directive definition you can use the "=" simbol to setup a bi-directional binding between your directive scope and your parent scope

module.directive('sap', function() {
return {
    restrict: 'E',
    replace: true,
    scope:{
      points:"=points"
    },
    link: function(scope, element, attrs) {
        var map = L.map(attrs.id, {
            center: [40, -86],
            zoom: 10
        });
        L.tileLayer('http://{s}.tile.cloudmade.com/57cbb6ca8cac418dbb1a402586df4528/997/256/{z}/{x}/{y}.png', {
            maxZoom: 18
        }).addTo(map);

        for (var p in points) {
            L.marker([p.lat, p.lng]).addTo(map);
        }
    }
};
});

Also instead of adding the markers right into the map, it's recomended to add your markers to a L.featureGroup first, and then add that L.featureGroup to the map because it has a clearLayers() metho, which will save you some headaches when updating your markers.

grupo = L.featureGroup();
grupo.addTo(map);

for (var p in points) {
    L.marker([p.lat, p.lng]).addTo(grupo);
}


// remove all markers
grupo.clearLayers();

I hope this helps, cheers

like image 1
Jorge Avila Avatar answered Nov 20 '22 19:11

Jorge Avila