-->

Dc.js leaflet marker popup showing fields from inp

2019-07-26 09:18发布

问题:

I'm a dc.js novice working on a dashboard displaying information on charts and a map.

I'm currently unable to display information on the marker popup window besides the default, which seems to be a point's coordinates (geo) and the number of occurrences. Current code is:

var facilities = xf.dimension(function(d) { return d.geo; });
var facilitiesGroup = facilities.group().reduceCount();

dc.leafletMarkerChart("#test .map",groupname)
  .dimension(facilities)
  .group(facilitiesGroup)
  .width(540)
  .height(440)
  .center([0,0])
  .zoom(7)
  .cluster(true)
  .filterByArea(true)
  .renderPopup(true)
  .popup();

I have tried to change the popup as follows:

  .popup(function(d,feature) { return feature.name +" : "+feature.items; });

To include the name of the location and the number of items there. However, the popups now just mention "undefined : undefined".

I'm sure that there's an easy solution I simply am ignoring due to my limited experience. Can anyone help?

回答1:

The heart of dc.js and crossfilter is sort of a map and reduce, where bins are found for each value, and then the rows that land in each bin are aggregated.

But it sounds like you are trying to directly display your data in the leaflet map without any aggregation. If you did want to aggregate, you'd have to decide how to aggregate items (you haven't shown your data so I don't know what it is), and probably have to leave off the name because names generally don't aggregate.

So I'd suggest telling crossfilter to bin by name (assuming that's unique) instead of location, and then hold onto the location and items, as well as keeping a count. Unfortunately this is somewhat at cross purposes to crossfilter's core purpose, so the code is a little bit awkward:

var facilities = xf.dimension(function(d) { return d.name; });
var facilitiesGroup = facilities.group().reduce(
    function(p, v) { // add
        p.items = v.items;
        p.geo = v.geo;
        ++p.count;
        return p;
    },
    function(p, v) { // remove
        --p.count;
        return p;
    },
    function() { // init
        return {count: 0};
    }
);

We're just assuming that the names are unique and grabbing the fields from the first record we see. Now the crossfilter group will return an array of key-value pairs, where name will be the key and the {count, items, geo} will be the value, so we can tell dc-leaflet how to read these:

dc.leafletMarkerChart("#test .map",groupname)
    // ...
    .valueAccessor(function(kv) {
        return kv.value.count;
    })
    .locationAccessor(function(kv) {
        return kv.value.geo;
    })
    .popup(function(kv) {
        return kv.key + " : " + kv.value.items;
    })

kv is a convention I like to use to remind myself that the data d is almost always going to be a key-value pair. (In some dc charts, it will be an object which contains a data member which is the key-value pair.)

Note that this is different from the dc-leaflet default behavior, to aggregate by location, and set the location as the key. If your locations are guaranteed to be unique, you could also do it that way, but it seems risky to me (data could get lost). I think it's better to use a truly unique key if you mean to display the original data without aggregation.



回答2:

The leafletMarkerChart did not work in my case. It showed no errors nor popups. Using the same approach on the standard dc-leaflet.js function you can do the following:

var restaurantsGroup = restaurantNames.group().reduce(
                    function(p, v) { // add
                    p.name = v.name;
                    p.price_range = v.price_range;
                    p.stars = v.stars;
                    p.latitude = v.latitude;
                    p.longitude = v.longitude;
            ++p.count;
            return p;
                },
                function(p, v) { // remove
                --p.count;
                return p;
                },
                function() { // init
                return {count: 0};
                }
            );


var marker = dc_leaflet.markerChart("#demo1 .map", groupname) //map formatting
      .dimension(restaurantNames)
      .group(restaurantsGroup)
      .width(700)
      .height(500)
      .center([43.733372, -79.354782])
      .zoom(11)
      .cluster(true) 
    .valueAccessor(function(kv) {
          return kv.value.count;
    })
    .locationAccessor(function(kv) {
          return [kv.value.latitude,kv.value.longitude] ;
     })
    .popup(function(kv,marker) {
          return kv.value.name + " - " + kv.value.stars + " * - "  + 
           kv.value.price_range + "$";
      });