angular-ui / ui-leaflet

AngularJS directive to embed an interact with maps managed by Leaflet library
http://angular-ui.github.io/ui-leaflet
Other
314 stars 137 forks source link

Control draw order (z-index) of layers and features #63

Open nmccready opened 8 years ago

nmccready commented 8 years ago

From @seadour on June 4, 2015 21:28

I cannot find a way to control the draw order (z-index) of layers and features with this directive.

I am trying to draw polygons from a geoJSON and a circle path. The geoJSON data is loaded via Angular $http GET method.

No matter what I do, the geoJSON polygons are always drawn on top of the circle path. I've tried changing the order of the path/geoJSON parameters in the $scope object, I've tried retrieving the geoJSON data first, I've tried changing the order of attributes in the map's HTML tag...nothing seems to affect it.

Sample code:

angular.extend($scope, {
    center: {
        lat: 38.5378,
        lng: -121.752,
        zoom: 17
    },
    layers: {
        baselayers: {
            mapbox: {
                name: 'Mapbox',
                url: 'http://api.tiles.mapbox.com/v4/[xxx]/{z}/{x}/{y}.png?access_token=[xxx]',
                type: 'xyz'
            }
        }
    },
    geojson: {},
    paths: {
        circle1: {
             latlngs: {
                 lat: 38.536,
                 lng: -121.753
             },
             radius: 100,
             type: 'circle'
        }
    }
});

$http({ method: 'GET',
    url: '/path/to/multipolygon.geo.json',
    cache: true
}).success(function(data) {
    angular.extend($scope, {
        geojson: {
            data: data,
            style: {
                fillColor: '#f9f9f9',
                weight: 0,
                fillOpacity: 1
        }
    });
});

If I were using 'vanilla' Leaflet.js without Angular, I could change the order simply by adding the geoJSON to the map object before I add the path, like so:

L.geoJson(data, {
    style: {
        fillColor: '#f9f9f9',
        weight: 0,
        fillOpacity: 1
    }
}).addTo(map);

L.circle([38.536, -121.753], 100).addTo(map);

In this screenshot, the grey buildings/roads are the basemap, the white building polygons are from the GeoJSON, and the blue circle is the circle path. I want the blue circle to draw on top of the white polygons.

screen shot 2015-06-04 at 2 27 05 pm

Copied from original issue: tombatossals/angular-leaflet-directive#788

nmccready commented 8 years ago

From @seadour on June 4, 2015 22:46

UPDATE: I found a workaround by defining the geoJSON inline as an object, with my controller, instead of retrieving it via $http GET. When I do this, the path draws on top of the geoJSON:

var testGeoJson = {
    "type": "BuildingPolygons",
        "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
        "features": [
            { "type": "Feature", "properties": { "Name": "Ghausi Hall"}, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -121.7532434, 38.536265 ], [ -121.753302, 38.5362549 ], [ -121.7533035, 38.5362603 ], [ -121.7533684, 38.5362492 ], [ -121.7533669, 38.5362438 ], [ -121.7533737, 38.5362426 ], [ -121.7533752, 38.536248 ], [ -121.7534401, 38.5362368 ], [ -121.7534386, 38.5362315 ], [ -121.7534454, 38.5362303 ], [ -121.7534469, 38.5362357 ], [ -121.7534743, 38.536231 ], [ -121.7534722, 38.5362245 ], [ -121.7534938, 38.5362208 ], [ -121.7534787, 38.5361682 ], [ -121.7534681, 38.53617 ], [ -121.7533536, 38.53577 ], [ -121.7533465, 38.5357712 ], [ -121.7533445, 38.5357641 ], [ -121.7533489, 38.5357628 ], [ -121.7533366, 38.535724 ], [ -121.7534167, 38.5357221 ], [ -121.7534247, 38.5357499 ], [ -121.7536083, 38.5357175 ], [ -121.7537247, 38.5361248 ], [ -121.7537077, 38.5361278 ], [ -121.7537248, 38.5361876 ], [ -121.7537022, 38.5361918 ], [ -121.753741, 38.5363313 ], [ -121.7536351, 38.5363495 ], [ -121.7536531, 38.5364139 ], [ -121.7533874, 38.5365185 ], [ -121.7532434, 38.536265 ] ] ] ] } },
            { "type": "Feature", "properties": { "Name": "Mathematical Sciences Building"}, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -121.7525651, 38.5359188 ], [ -121.7525653, 38.5358625 ], [ -121.7525531, 38.5358625 ], [ -121.7525532, 38.5358364 ], [ -121.7525654, 38.5358364 ], [ -121.7525658, 38.5357187 ], [ -121.7531007, 38.5357198 ], [ -121.7531004, 38.5358094 ], [ -121.7531406, 38.5358095 ], [ -121.7531404, 38.535874 ], [ -121.7531316, 38.535874 ], [ -121.7531315, 38.5358963 ], [ -121.7531001, 38.5358963 ], [ -121.7531, 38.5359292 ], [ -121.7528011, 38.5359286 ], [ -121.7528007, 38.536025 ], [ -121.7522728, 38.5360239 ], [ -121.7522727, 38.5360517 ], [ -121.7522273, 38.5360516 ], [ -121.7522275, 38.5359871 ], [ -121.7522345, 38.5359871 ], [ -121.7522346, 38.5359648 ], [ -121.7522678, 38.5359649 ], [ -121.752268, 38.5359182 ], [ -121.7525651, 38.5359188 ] ] ] ] } }
        ]
    };

...

angular.extend($scope, {
    ...
    geojson: {
        data: testGeoJson,
        style: {
            fillColor: '#f9f9f9',
            weight: 0,
            fillOpacity: 1
        }
    }
});

screen shot 2015-06-04 at 3 43 49 pm

Still would be preferable to load the geoJSON via GET and specify its draw order to be before the circle path.

nmccready commented 8 years ago

I think your or someone is going to need to dig into leaflet for a clean approach. On your backend you could sort the order of your geojson elements so that they are they way you want as well.

nmccready commented 8 years ago

http://stackoverflow.com/questions/29318078/geojson-layer-order-leaflet

So GeoJson inherits FeatureGroup so we could look to bring things forward or send them back based on some preference. It would be a total pain in the rear to try and specify a real zIndex for it. This is because each layer has a specific zIndex which messing with can Fubar leaflet all together.

The easiest way to do the referencing of order (front to back) would be adding this functionality in geojsonnested. This is because when nesting a new GeoJSON object is created for each nested item which we can reference and keep track of its ordering. One caveat to this is using geojsonnested is usually more convienient if you have two individual services (say one for circles and another for polygons).

If you wanted this non-nested then we would have to sort the Polygons vs Circles etc just like you would on the backend (backend would be faster). So why bother here?

nmccready commented 8 years ago

Thoughts?

nmccready commented 8 years ago

From @seadour on June 5, 2015 16:20

Sorry @nmccready, I think a lot of that went over my head. When you say "on the backend", do you mean in my controller using the leafletData object provided by the directive?

nmccready commented 8 years ago

From @seadour on June 5, 2015 16:30

I tried using this:

leafletData.getPaths().then(function(paths) {
    paths.bringToFront();
});

but it doesn't seem to work.

nmccready commented 8 years ago

From @seadour on June 10, 2015 20:43

Thoughts?

nmccready commented 8 years ago

Your using the geojson directive not paths directive. You're grabbing the wrong domain you need leafletData.getGeoJson. It is the inverse of what was set here

nmccready commented 8 years ago

From @seadour on June 10, 2015 22:14

I am using both a GeoJson and circle paths. Remember that I am trying to bring my circle paths in front of the GeoJson, not the other way around.

The way I can see it, I have two options:

1) Bring the GeoJson to the back:

leafletData.getGeoJSON().then(function(geojson) {
    geojson.bringToBack();
});

This works, but then it's behind my map tiles so I can't see it at all.

2) Bring the circle paths to the front:

leafletData.getPaths().then(function(paths) {
    paths.bringToFront();
});

But as I already said, this does not work.

nmccready commented 8 years ago

http://leafletjs.com/reference.html#path-bringtofront

Docs says it only brings the paths to front of all other paths, not another layer.