Integrate any D3.js chart with DC.js Library

231 views
Skip to first unread message

vivek raju

unread,
Jan 23, 2020, 12:48:51 AM1/23/20
to dc-js user group
I have been playing around with the Dc.js library for sometime now. The library is great but I've been trying to integrate custom D3.js charts with Dc.js Library. I have read a couple of posts about this where a suggestion was made to register the chart with the dc.chart registry. I did not quite understand how this all worked. Can someone tell me how could i go about to achieve this?

Gordon Woodhull

unread,
Jan 24, 2020, 8:08:49 AM1/24/20
to dc.js user group
Hi Vivek!

I added an example of the simplest custom chart I could think of, using ES6:

http://dc-js.github.io/dc.js/examples/custom-chart.html

Really you just need an object that implements .render() and .redraw(), and use dc.registerChart it when you initialize (here in the constructor).

The comments in the old answer that has been serving as the how-to 


show some other methods you might need to implement, depending on other features you use.

(I regard these as bugs, but it is easy enough to work around so there hasn't been motivation to fix them.)

Cheers,
Gordon

Gordon Woodhull

unread,
Jan 24, 2020, 8:55:46 AM1/24/20
to dc-js user group
This example doesn't cover pulling data from crossfilter groups (or dimensions), or how to apply a filter, but I wanted to keep it simple.

You can take a look at how these things are implemented in the source.

Feel free to ask more questions as you continue your journey!


vivek raju

unread,
Jan 24, 2020, 12:59:52 PM1/24/20
to dc-js user group
Hey Gordon,
Could you please add the source code in the master branch? Thanks for the reply, this is what i was actually looking for.

Gordon Woodhull

unread,
Jan 24, 2020, 1:02:01 PM1/24/20
to dc.js user group
Great! It's on the develop branch. It will get merged to master when we make the next release.



--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dc-js-user-group/b91a43bc-323f-4dba-81be-45322a8c414d%40googlegroups.com.

Message has been deleted

Gordon Woodhull

unread,
Jan 27, 2020, 5:40:30 AM1/27/20
to dc-js-us...@googlegroups.com
This example uses DCv4. But I think it should work with v3 if you change

new dc.BarChart(...)

to

dc.barChart(...)

(To be precise, the second syntax works with both versions, but the first is recommended syntax for v4.)

On Jan 27, 2020, at 12:40 AM, vivek raju <rapsta...@gmail.com> wrote:

On Friday, January 24, 2020 at 11:32:01 PM UTC+5:30, Gordon Woodhull wrote:
Great! It's on the develop branch. It will get merged to master when we make the next release.

On Jan 24, 2020, at 12:59 PM, vivek raju <rapsta...@gmail.com> wrote:

Hey Gordon,
Could you please add the source code in the master branch? Thanks for the reply, this is what i was actually looking for.

On Friday, January 24, 2020 at 6:38:49 PM UTC+5:30, Gordon Woodhull wrote:
Hi Vivek!

I added an example of the simplest custom chart I could think of, using ES6:

http://dc-js.github.io/dc.js/examples/custom-chart.html

Really you just need an object that implements .render() and .redraw(), and use dc.registerChart it when you initialize (here in the constructor).

The comments in the old answer that has been serving as the how-to 


show some other methods you might need to implement, depending on other features you use.

(I regard these as bugs, but it is easy enough to work around so there hasn't been motivation to fix them.)

Cheers,
Gordon


--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-us...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.

Marquisio

unread,
Apr 21, 2021, 2:42:49 PM4/21/21
to dc-js user group
Hi, right now I'm dealing with the same problem of integrating my d3 hexbin chart to dc.js, so I guess it is ok to expand this conversation instead of making a new one. I found older stackoverflow questions regarding this topic. I get the general idea of registerChart and implementing my own render() and redraw() methods, but I don't know how to start with that. My main problem of understanding is the way of creating chart instance/object for registry. Can that object be already implemented chart in d3.js or it has to be some kind of general object for that kind of chart ? Any help or tip would be really appreciated. 

P.S I tried to reach source code of https://dc-js.github.io/dc.js/examples/custom-chart.html, but I guess it doesn't exist anymore

Gordon Woodhull

unread,
Apr 22, 2021, 9:41:09 AM4/22/21
to dc-js-us...@googlegroups.com
We should add redirects on moved examples.

That simple example is now here:


Yes, you implement the chart object. It is a dc.js idiom.

Here is a more complex example (but kind of weird and specific):


A dc.js-compatible hexbin chart would be really cool. I hope you consider open sourcing it!

Sorry I don’t have a complete tutorial on creating custom charts. Glad to answer any questions.


On Apr 21, 2021, at 2:43 PM, Marquisio <jelo...@gmail.com> wrote:

Hi, right now I'm dealing with the same problem of integrating my d3 hexbin chart to dc.js, so I guess it is ok to expand this conversation instead of making a new one. I found older stackoverflow questions regarding this topic. I get the general idea of registerChart and implementing my own render() and redraw() methods, but I don't know how to start with that. My main problem of understanding is the way of creating chart instance/object for registry. Can that object be already implemented chart in d3.js or it has to be some kind of general object for that kind of chart ? Any help or tip would be really appreciated. 

Marquisio

unread,
Apr 22, 2021, 10:41:47 AM4/22/21
to dc-js user group
Thank you Gordon. I will look into it, and hopefully come up with a good enough solution. Open sourcing it would be great.

Best regards.

Marquisio

unread,
Apr 23, 2021, 5:23:42 AM4/23/21
to dc-js user group
Hi, I have another question regarding plotting data on my hexbin chart. I'm little bit confused on how to do it. Let's say I have this way of doing it in d3.js https://www.d3-graph-gallery.com/graph/density2d_hexbin.html where I store my data in "inputForHexbin" and later on plotting them with ".data(hexbin(inputForHexbinFun))". In dc.js I call chart object functions .dimension() and .group() which contain data in them. So my question is, is there a way to keep this kind of approach as in d3.js version for plotting data ?. I'm not sure how to get the data from .dimension() and .group() or what will be the best approach for plotting hexbin chart in dc.js.

Gordon Woodhull

unread,
Apr 24, 2021, 9:13:47 AM4/24/21
to dc.js user group
Hi Marquisio,

I think it will be easy to have your hexbin driven by crossfilter and filtering on the other charts, but it will be more difficult to allow filtering by clicking on the hex cells.

Normally in response to .render() or .redraw() you would use group.all() to fetch the aggregated data. However, d3-hexbin doesn't have any way to set the value/count of each point, so the data can't be pre-aggregated by crossfilter.

Instead, you could use crossfilter.allFiltered() to fetch only the filtered rows, and pass them to hexbin.data(). Then it will be driven by filters on the other charts.

If you wanted the hex cells to be clickable, you would have to extract the shape data from hexbin.hexagon() (or deeper in d3-hexbin, to avoid parsing the SVG path string) and write a filterFunction that accepts any points within the selected cells.

Hope this helps. Glad to iterate on it if you get stuck.

Cheers,
Gordon

Marquisio

unread,
Apr 25, 2021, 6:44:30 AM4/25/21
to dc-js user group
Right now I'm stuck with part where I can't brush my hexbin chart. I guess it is not correctly synced with dc.js (but chart is here, in my dashboard). I extended CoordinateGridMixin to my chart which should give me brush option. If I'm thinking correctly, probably it has something to do with <svg> and <g> elements that my chart is not fully synced. I tried to use as much as I can functions like width(), height() and x(), and also tried to reach internal <svg> that was made from that, but no success. Am I missing something crucial ? Sorry if I'm all over the place with questions. 

Gordon Woodhull

unread,
Apr 25, 2021, 9:34:20 AM4/25/21
to dc-js-us...@googlegroups.com
I guess you are saying that you got the chart to display. Congratulations!

I don’t think you can just extend CoordinateGridMixin to add brushing to this chart - that’s really for x-y charts and it’s a pretty complicated system. These aren’t really mixins like “mix and match” but a framework of base classes with lots of things you need to override.

Like I said before, you could allow clicking on the cells if you use filterFunction.

How do you want selection to work?

On Apr 25, 2021, at 6:45 AM, Marquisio <jelo...@gmail.com> wrote:

Right now I'm stuck with part where I can't brush my hexbin chart. I guess it is not correctly synced with dc.js (but chart is here, in my dashboard). I extended CoordinateGridMixin to my chart which should give me brush option. If I'm thinking correctly, probably it has something to do with <svg> and <g> elements that my chart is not fully synced. I tried to use as much as I can functions like width(), height() and x(), and also tried to reach internal <svg> that was made from that, but no success. Am I missing something crucial ? Sorry if I'm all over the place with questions. 

Marquisio

unread,
Apr 25, 2021, 9:51:23 AM4/25/21
to dc-js user group
I thought brush is something that could be easily used here if I extend CoordinateGridMixin. I also have a histogram which can be burhsed so i thought hexbin chart would behave the same way (in histogram I have bars, and here I have hexbins).  I'm making a bigger dashboard with multiple charts, but only hexbin is one that is not directly compatible with dc. So my idea is just that it blends with everything else. For selection, probably is better to click on the cells, but brushing it like a scatter plot is also ok. 

Gordon Woodhull

unread,
Apr 25, 2021, 10:06:46 AM4/25/21
to dc-js-us...@googlegroups.com
IMO the tricky thing about a rectangular brush on a hexbin chart is that the rectangle won’t align in any way with the hex cells, so it won’t be clear what is selected and what is not. 

That said, if you want rectangular brushing, I’d advise you to look at d3-brush examples and try to integrate that into your code. CoordinateGridMixin is really complicated and does much more than just brushing.

Once you have brushing working visually, you should be able to apply the filter using

Like you say, it should work pretty much like a scatter plot, except that you can’t see the individual points.


On Apr 25, 2021, at 9:52 AM, Marquisio <jelo...@gmail.com> wrote:

I thought brush is something that could be easily used here if I extend CoordinateGridMixin. I also have a histogram which can be burhsed so i thought hexbin chart would behave the same way (in histogram I have bars, and here I have hexbins).  I'm making a bigger dashboard with multiple charts, but only hexbin is one that is not directly compatible with dc. So my idea is just that it blends with everything else. For selection, probably is better to click on the cells, but brushing it like a scatter plot is also ok. 

Marquisio

unread,
Apr 25, 2021, 10:19:47 AM4/25/21
to dc-js user group
If I decide to go with clicking on a cell, is a heatmap example good enought to reference on, because in it we are clicking rectangles ? 

Gordon Woodhull

unread,
Apr 25, 2021, 10:31:15 AM4/25/21
to dc-js-us...@googlegroups.com
Not sure what you mean. 

I would look for a clickable or hoverable hexbin example, maintain a list of selected cells, and then implement filtering using the coordinates and shapes provided by d3-hexbin.

Maybe this example will help, it has clicking:


On Apr 25, 2021, at 10:20 AM, Marquisio <jelo...@gmail.com> wrote:

If I decide to go with clicking on a cell, is a heatmap example good enought to reference on, because in it we are clicking rectangles ? 

Marquisio

unread,
Apr 28, 2021, 3:01:59 PM4/28/21
to dc-js user group
Hi Gordon, I decided two go with rectangular brush and applied RangedTwoDimensionalFIlter. Now I tried calling functions replaceFilter() and redrawGroup() inside my component (just like it is done in scatter plot), but I'm getting errors of undefined function. I don't know is it something regarding javascript ( I tried all cases of fixing it, if it is javascript problem) or I can't call this function like that.

Gordon Woodhull

unread,
Apr 29, 2021, 9:30:18 AM4/29/21
to dc-js-us...@googlegroups.com
Can you share a running example via jsfiddle, codepen, observable, etc, or your own website?

I can’t diagnose this without at least looking at your code. Running code would be much better.


On Apr 28, 2021, at 3:02 PM, Marquisio <jelo...@gmail.com> wrote:

Hi Gordon, I decided two go with rectangular brush and applied RangedTwoDimensionalFIlter. Now I tried calling functions replaceFilter() and redrawGroup() inside my component (just like it is done in scatter plot), but I'm getting errors of undefined function. I don't know is it something regarding javascript ( I tried all cases of fixing it, if it is javascript problem) or I can't call this function like that.

Marquisio

unread,
Apr 29, 2021, 11:33:26 AM4/29/21
to dc-js user group
Sorry I don't know how to properly make running example in one of this editors because my code is pretty complex in general. I give you here a whole component in which I'm making hexbin chart. If it is not enough for you to understand everything, I can try to make running example in jsfiddle.
So during brush I call function brushmove() in which I call filters.RangedTwoDimensionalFilter() for every brush selection. And after that I try to call replaceFilter() and redrawGroup(). At that moment error is raised saying "this.replaceFilter() is not a function".  

import '../App.css';
import React from 'react'
import * as d3 from 'd3'
import * as d3Hexbin from 'd3-hexbin'
import * as crossfilter from 'crossfilter'
import * as dc from 'dc'
import {ChartTemplate} from './chartTemplate'
import { brush, brushX, svg } from 'd3'
import {BaseMixin, CoordinateGridMixin, StackMixin, filters, events, constants} from 'dc';


export class HexBinChart extends BaseMixin {

    constructor(parent, group) {

        super();
       
        this._colors = null;
        this._seriesAccessor = null;
        this._color = null;
        this._hexbin = null;
        this._cell = null;
        this._svg = null;
        this._brush = null
        this._root = d3.select(parent)
        console.log(this._root)
        dc.registerChart(this, group)
        //console.log(dc.chartRegistry.list())
        this.anchor(parent, group);

        this.replace = this.replace.bind(this)
        console.log(this)
        //this.makeScatterPlot = this.makeScatterPlot.bind(this)
    }

    brushmove() {

        var e = d3.brushSelection(this)
        console.log(e)

        if(e) {
            d3.selectAll(".hex").style("fill-opacity", 1)
            var ranged2DFilter = filters.RangedTwoDimensionalFilter(e)
            
            this.replaceFilter(ranged2DFilter);
            this.redrawGroup();
          
        }
    }

    render() {

        var margin = { top: 10, right: 30, bottom: 30, left: 60 },
           width = 560 - margin.left - margin.right,
            height = 500 - margin.top - margin.bottom;

        console.log(this._root)
        console.log(super.svg())
        this.resetSvg()
        this._svg = d3.select("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform",
                "translate(" + margin.left + "," + margin.top + ")")
        console.log(this._svg)
        this._svg.append("image").attr("width", width).attr("height", height+20).attr("xlink:href", "./ShotChart.png").attr("transform", "scale(1, 1)")
        // Add X axis
        this._x = d3.scaleLinear()
        .domain([-250, 250])
        .range([0, width]);
        this._svg.append("g")
        .attr("transform", "translate(0," + height + ")").attr("visibility", "hidden")
        .call(d3.axisBottom(this._x));

        // Add Y axis
        this._y = d3.scaleLinear()
        .domain([-50, 470])
        .range([height, 0]);
        this._svg.append("g").attr("visibility", "hidden")
        .call(d3.axisLeft(this._y));

        this._color = d3.scaleLinear()
        .domain([0, 20])
        .range(['transparent', "#69b3a2"])

        this._hexbin = d3Hexbin.hexbin().radius(10)
        .extent([[0, 0], [width, height]])

        // Plot the hexbins
        this._svg.append("clipPath")
        .attr("id", "clip")
        .append("rect")
        .attr("width", width)
        .attr("height", height)

        this._path = this._svg.append("g")
        this._brush = d3.brush().extent([[0,0], [470,480]])

        this.redraw()
        
    }

    redraw() {

        const data = this.data()
        var inputForHexbin = []

        var x = this._x
        var y = this._y
        var color = this._color
        //console.log(data)
        data.forEach((d) => {
                const isFiltered = (d.value > 0) //plot only brushed data, or all
                if (isFiltered) {
                inputForHexbin.push([x(d.key[0]), y(d.key[1])])}
            })

        
        this._svg.selectAll(".hexbin").remove() //remove old plot g element
        this._svg.selectAll(".brush").remove()
        //console.log(inputForHexbin)
        var path = this._svg.append("g")
            .attr("clip-path", "url(#clip)")
            .attr("class", "hexbin")
            .selectAll("path")
            .data(this._hexbin(inputForHexbin))
        path.enter().append("path")
            .attr("class", "hex")
            .attr("d", this._hexbin.hexagon())
            .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
            .attr("fill", function (d) { return color(d.length); })
            .attr("stroke", "black")
            .attr("stroke-width", "0.1")

        path.exit().remove()
       

        function brushStart(){

            d3.select(".brush").call(this._brush.move, null);
        }

        
        this._svg.append("g").attr("class", "brush").call(this._brush.on("start brush end", this.brushmove))
    }

    
}

const shotChartFunc = (divref, ndx) => {

    var locDimension = ndx.dimension(function (data) {
        return [data.LOC_X, data.LOC_Y]
    });

    
    var hgroup = locDimension.group()
  
    var shotChart = new HexBinChart(divref)
    console.log(shotChart)
    shotChart.dimension(locDimension).group(hgroup)
    return shotChart

}

export const ShotChart = props => (
    <ChartTemplate chartFunction={shotChartFunc}/>
)

Marquisio

unread,
Apr 29, 2021, 2:32:16 PM4/29/21
to dc-js user group
When I call replaceFilter like super.replaceFilter(ranged2dFilter) it goes into function but know error is inside the function on this._resetFilterHandler saying it is not function. That is part I can't really control know. It seems I didn't really understand how to work with this mixins.

Gordon Woodhull

unread,
Apr 29, 2021, 2:55:53 PM4/29/21
to dc.js user group
Thanks for sharing your code.

I don't see why that would happen, just staring at the code. BaseMixin initializes this._resetFilterHandler in its constructor, which you are calling with super().

Next thing you might look at is whether any of the many fields that BaseMixin initializes are present on your object. I don't see how it could happen, but is `this` not the expected object somehow?

Deriving from BaseMixin adds a layer of complexity which you might not really need at this point. If you just implement the chart interfaces yourself you might have an easier time.

Again, I'd be glad to help you debug this further if you figure out how to share the live code with me. "Jedi debugging", just reading the code and trying to visualize what might be going wrong, is really hard!


Marquisio

unread,
May 10, 2021, 6:53:47 AM5/10/21
to dc-js user group
I managed to integrate my chart to dc.js. Everything works except one thing. In my hexbin chart I'm getting data to plot through this.data(). When one of dc charts is brushed I can see which data is filtered with value=0 or value>0  inside this.data(). But when I brush hexbin chart I cant see which data is filtered (there is no data with value=0 inside this.data()). Is there a way to know which data is filtered when I brush hexbin chart so that I can take proper actions on it ? 

Gordon Woodhull

unread,
May 10, 2021, 4:46:45 PM5/10/21
to dc.js user group
Hi Marquisio,

I'm glad you got your chart working! I hope you share the code when it's complete.

So are you are saying that adding the RangedTwoDimensionalFilter when the brush is active does not have any effect on the filtering? The other charts don't change?

Or are you asking how to fetch just the filtered rows? As mentioned earlier, crossfilter.allFiltered() does this, and you can tell it dimensions to ignore, if desired.

Cheers,
Gordon

Marko Jelovic

unread,
May 10, 2021, 5:13:59 PM5/10/21
to dc-js-us...@googlegroups.com

All the charts change when one is brushed. What I want to have is that when I brush hexbin chart it automatically greys out hexbins that are not inside brush rect. I'm currently using this.data() to extract data. When other charts are brushed my hexbin is filtered fine because I can recognize in this.data() which values are filtered. Only issue is when I brush hexbin chart I cant recognize on its side which data is filtered because now this.data() doesn't have any value=0 which indicates if data is filtered. Sry if it is complicated to understand because only that one case is not working properly.

Best regards.


Dana 10. svi 2021. 22:46 osoba "Gordon Woodhull" <gor...@woodhull.com> napisala je:
Hi Marquisio,

To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-group+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-group+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dc-js-user-group/EC24ABEF-3C87-4A1E-9076-C8CDC7F8DCA4%40woodhull.com.

matthew lange

unread,
May 23, 2021, 9:19:47 PM5/23/21
to dc-js-us...@googlegroups.com
Ciao Marko, it sounds like you have things up and running. To Gordon's point, I think it would be useful for the community from which you benefited, to share your code. This way we can advance the technology together. I for one, would really like to see how you took your own D3.js chart and linked it into a DC.js visualization...

Hi Marquisio,

To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dc-js-user-group/EC24ABEF-3C87-4A1E-9076-C8CDC7F8DCA4%40woodhull.com.

--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.

Gordon Woodhull

unread,
May 24, 2021, 10:49:55 AM5/24/21
to dc.js user group
Hi Marko,

Matthew's kind note made me notice that I had missed your last reply.

Typically in dc.js a chart will not observe its own dimension's filters. This is the way crossfilter is designed.

You usually don't want, e.g. a bar chart to drop to zero outside of the brushed area. Instead, there are visual clues like the brush, and selected bar style.

However, if you are using crossfilter.allFiltered(), you should be seeing all filters, including the one you are setting via the hexbin chart. So I wonder if your filter is working correctly.

Note that since rectangular selection will never align perfectly with hexbin, the cells on the boundary will usually be "partly" selected. You'd have to map every data point to its center before computing the filter if you want cells to be binary selected / not selected.

Cheers,
Gordon


Marquisio

unread,
May 24, 2021, 4:03:59 PM5/24/21
to dc-js user group
Hi
Yes, my chart is running as it should be. I was hoping to make a general example of integrating d3.js chart to dc.js once I finish with my whole project, but unfortunately that won't be very soon. For now I can only share this concrete part which integrates my hexbin chart to dc.js, but it won't have any meaning on it's own since it's part of something more complexed, except the idea that is behind integration. I covered the parts that are related to integration with bolded comments so that is easier to understand what is going on.

You are right Gordon about "partly" selected bins. That is something I will have to find a workaround. Also thank you for all your help and tips. I hope that this can help someone in task of integrating d3.js chart to dc.js.
Maybe at some point during next month I can make more general, runnable representation of this.

NOTE: This is implemented with React.js

//BaseMixin is important to extend because it has base functions like render(), redraw() filter() and redrawGroup() which will be heavily used here
export class HexBinChart extends BaseMixin {

    constructor(parent, ndx, group) {

        super();
          
        this._colorInit = null;
        this._colorBrushed = null;
        this._hexbin = null;
        this._radius = null;
        this._svg = null;
        this._brush = null
        this._selected = null
        this._path = null
        this._ndx = ndx
        this._initData = null
        this.inputForHexbin_brushed = []
        this.inputForHexbin_init = []
        this._stop = false 

        //The most important thing, register your chart inside group
        //anchor() function also register chart inside it so I guess it doesn't matter which function you use
        this.anchor(parent, group);
        dc.registerChart(this, group)
     
        this.send = this.send.bind(this)
        
    }

    //This function replaces current filter with the new one and calls redrawGroup() so that all other charts in the group can update themselves once the hexbin chart is brushed
    send(value) {
   
            var ranged2DFilter = filters.RangedTwoDimensionalFilter(value)
            
            events.trigger( () => {
                this.replaceFilter(ranged2DFilter);
                this.redrawGroup();
               
            }, constants.EVENT_DELAY)
 
    }
    
    //All the charts inside the group need to render themselves first. As you can see, this is pure d3.js in which the "skeleton" of the chart is made, and is called only once

    render() {

        var margin = { top: 10, right: 30, bottom: 30, left: 60 },
           width = 460 - margin.left - margin.right,
            height = 400 - margin.top - margin.bottom;

        var that = this
       
        this.resetSvg()
      
        // append the svg object to the body of the page
        this._svg = d3.select("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform",
                "translate(" + margin.left + "," + margin.top + ")")

       
        
        // Add X axis
        this._x = d3.scaleLinear()
        .domain([-250, 250])
        .range([0, width]);
        this._svg.append("g")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(this._x));

        // Add Y axis
        this._y = d3.scaleLinear().range([height, 0])
        .domain([-50, 470]);
        this._svg.append("g")
        .call(d3.axisLeft(this._y));

        
        this._colorInit = d3.scaleLinear()
        .domain([0, 30])
        .range(['transparent', "red", "#8B0000"])
        

        this._colorBrushed = d3.scaleLinear()
        .domain([0, 30])
        .range(['transparent', "steelblue"])

        this._hexbin = d3Hexbin.hexbin().radius(10)
        .extent([[0, 0], [width, height]])

        this._radius = d3.scaleLinear()
        .domain([0, 1])
        .range([0, 10]);

        this._svg.append("clipPath")
        .attr("id", "clip")
        .append("rect")
        .attr("width", width)
        .attr("height", height)

        this._path = this._svg.append("g")
        this._brush = d3.brush().extent([[0,0], [width,height]])

        this._path = this._svg.append("g")
            .attr("clip-path", "url(#clip)")
            .attr("class", "hexbin")
            .selectAll("path")

       //Brushed data is sent to send() function, or the brush can be reset in if statement (with calling .filter(null) and then redrawGroup() for updating all other charts)
       function brushmove(){
    
            var e = d3.brushSelection(this)

            if(e[0][0] === e[1][0] & e[0][1] === e[1][1]){
 
                that.filter(null)         
                that.redrawGroup()
            }
           
            else{
                    
                var brushed = [[that._x.invert(e[0][0]), that._y.invert(e[0][1])],[that._x.invert(e[1][0]), that._y.invert(e[1][1])]]                 
                that.send(brushed)                
            }
        }
            
        this._svg.append("g").attr("class", "brush").call(this._brush.on("start brush",brushmove))

        //Once the render part is over, we automatically call initial redraw()
        this.redraw()
        
    }

    // Redraw() function is important for plotting our points/hexbins to chart. Also this function is called whenever some changes appear, in form of a new filter, inside our group
    redraw() {

        var that = this
        var x = this._x
        var y = this._y
        
        //By using crossfilter function .allFiltered() we can always get the latest filtered data from our group of charts.
        if(!this._stop){
            this._initData = this._ndx.allFiltered()
            this._stop = true
        }
        const newData = this._ndx.allFiltered()
        this.inputForHexbin_brushed = []
        this.inputForHexbin_init = []
      
        this._svg.selectAll(".hex").remove()
        this._svg.selectAll(".hex_brushed").remove()

        this._initData.forEach((d) => {
            this.inputForHexbin_init.push([x(d.LOC_X), y(d.LOC_Y)])
        })

        var colorInit = this._colorInit
        var colorBrushed = this._colorBrushed
        
        this._path.data(this._hexbin(this.inputForHexbin_init))
        .enter().append("path")
            .attr("class", "hex")
            .attr("d", this._hexbin.hexagon())
            .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
            .attr("fill", function (d) { return colorInit(d.length); })
            .attr("stroke", "black")
            .attr("stroke-width", "0.5")
        
        var init_hexbins = this._hexbin(this.inputForHexbin_init)
        this.inputForHexbin_brushed = []
        if(!(this._initData.length === newData.length)){
            newData.forEach((d) => {
                this.inputForHexbin_brushed.push([x(d.LOC_X), y(d.LOC_Y)])
            })
        }

        this._path.data(this._hexbin(this.inputForHexbin_brushed))
        .enter().append("path")
            .attr("class", "hex_brushed")
            .attr("d", function(d) { return that._hexbin.hexagon(that._radius(findHex(init_hexbins, d))) })
            .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
            .attr("fill",  function (d) {return colorBrushed(findColor(init_hexbins, d)); })
            .attr("stroke", "black")
            .attr("stroke-width", "0.5")
        
        function findHex(initHexbins, newHex){

            var pc = 0
            initHexbins.forEach(function(d) {

                if(d.x === newHex.x && d.y === newHex.y){

                    pc = newHex.length/d.length
                    return pc
                }
            })
            return pc

        }

        function findColor(initHexbins, newHex){

            var sum = 0
            initHexbins.forEach(function (d) {

                if(d.x === newHex.x && d.y === newHex.y){

                    sum = d.length
                    return sum
                }
            })
            return sum
        }   
    }
}

//In this part a new object of hexbinChart is made

const shotChartFunc = (divref, ndx) => {

    var locDimension = ndx.dimension(function (data) {
        return [data.LOC_X, data.LOC_Y]
    });

    var hgroup = locDimension.group()
    var myChart = new HexBinChart(divref, ndx, hgroup.top(Infinity))

    myChart.dimension(locDimension).group(hgroup)
    return myChart

}

export const MyChart = props => (
    <ChartTemplate chartFunction={myChartFunc}/>
)

    

Hi Marquisio,

To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages