Thanks to visit codestin.com
Credit goes to github.com

Skip to content

GeoChoropleth refactor #719

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

Conversation

mtraynham
Copy link
Contributor

Kind of a large refactor, which adds:

  • Auto zoom to geo features with the current projection
  • Graticule
  • Sphere
  • Title accessor for features
  • Simplifies render and redraw functions

TODO:

  • Fix tests (changes to SVG class names don't match)
  • Add moar tests!
  • Document

Future:

var _projectionTween = function (projectionA, projectionB, width, height) {
    return function (d) {
        var t = 0,
            projection = d3.geo.projection(function (λ, φ) {
                λ *= 180 / Math.PI;
                φ *= 180 / Math.PI;
                var p0 = projectionA([λ, φ]),
                    p1 = projectionB([λ, φ]);
                return [(1 - t) * p0[0] + t * p1[0], (1 - t) * -p0[1] + t * -p1[1]];
            }).scale(1).translate([width / 2, height / 2]),
            path = d3.geo.path().projection(projection);

        return function (_) {
            t = _;
            return path(d);
        };
    };
},

Strikethroughs have been completed.

@gordonwoodhull gordonwoodhull added this to the v2.1 milestone Oct 2, 2014
@gordonwoodhull
Copy link
Contributor

Cool! Is this change interface-compatible? Not that it matters. I'll queue this up for the 2.1 release (and if we switch to gitflow I'll be able to merge it to a develop branch once you've written tests).

There should no license concerns with Jason Davies' code - it's BSD licensed like d3. I wonder if it's distributed anywhere. We pulled in his box plot implementation from d3-plugins but at a glance it doesn't look like he's published d3.geo.zoom there.

@mtraynham
Copy link
Contributor Author

By interface compatible, do you mean with leaflet.js?

So two things about the projection transitions vs. projection zooms. I couldn't get them to work together (also with the automatic zoom to projection). There was just too much path-projection manipulation going on at the same time.

I was however able to get d3.geo.zoom to work with the implementation I have checked in. I was unsure about Davies license because it was from his personal blog and not posted anywhere. This is more or less the code for the zooming, which I was able to integrate fairly easily.
http://www.jasondavies.com/maps/rotate/d3.geo.zoom.js

@mtraynham
Copy link
Contributor Author

Also, I updated the web demo so you can try out the new features.

@gordonwoodhull
Copy link
Contributor

His code links to a LICENSE.txt in the same directory which is BSD, so we're good there.

We should link to his source and expand the license link in our copy.

As for interface compatibility, I was asking whether existing uses of geoChoropleth will be broken by the changes. It sounds like at least CSS customizations might get lost.

It's fine to change the interface with a version bump (we won't try to cram this into 2.0) but I'm asking out of curiosity, and it would be nice to document any changes to help folks migrate.

@mtraynham
Copy link
Contributor Author

Sounds good. Do you like the idea of a CHANGELOG.md file? Might be good for the 2.0 bump.

@seriousben
Copy link

Excited about this :) Do you have any examples you could share?

@gordonwoodhull
Copy link
Contributor

Sure.. but maybe we should start the changelog after 2.0 - I don't even know half the things that went into 2.0.

@mtraynham
Copy link
Contributor Author

@seriousben You can check out my fork and use the 'geo-refactor' branch. After you run npm install, run grunt server. Navigate to http://0.0.0.0:8888/web/vc/index.html for the demo USA map. To try out some of the features, change the usChart in web/vc/index.html with something like this:

            usChart.width(990)
                    .height(500)
                    .dimension(states)
                    .group(stateRaisedSum)
                    .colors(d3.scale.quantize().range(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF", "#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"]))
                    .colorDomain([0, 200])
                    .colorCalculator(function (d) { return d ? usChart.colors()(d) : '#ccc'; })
                    .addLayer(statesJson.features, "state", function (d) {
                        return d.properties.name;
                    })
                    .showGraticule(true)
                    .showSphere(true)
                    .zoomable(true)
                    .title(function (d) {
                        return "State: " + d.key + "\nTotal Amount Raised: " + numberFormat(d.value ? d.value : 0) + "M";
                    });

After trying it, I noticed there is something strange with the auto zoom. I think it's related to the GeoJSON and bounding box calculation, rather than the code itself because it does fit the view, but not as you would expect.

@mtraynham
Copy link
Contributor Author

Alrighty, I just fixed the web demo by using a fresh copy of the US states from Natural Earth (public domain license). Also required me to change the key accessor on the layer to return d.properties.postal instead of name.

_chart._doRedraw = function () {
var data = _layeredData(_chart.data());

for (var layerName in _layers) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mtraynham, does this cover the layer-removed case? I haven't reviewed this thoroughly, but thought of this case because I was looking at #698.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not since we never bind the layer data using d3 and instead just loop them. Should be a fairly easy change.

@mtraynham
Copy link
Contributor Author

Hey Gordon,
It does not, a redraw is required when the layers have been updated. It's an interesting suggestion. A render (re)creates the paths it thinks is necessary (layers/sphere/graticule) and sets up the zoom. Then it calls redraw which handles the data from crossfilter, title updates, projection changes, color updates... When I hear redraw, I associate that to what needs to be updated when you see a filter happen.

My thoughts are:

  • Should we be removing layers that Crossfilter has no data for?
  • What do you expect to be updated often?
    • How often do you remove/add layers?
    • This process could be handled in redraw by removing certain paths and adding new ones, BUT you are adding a step to a commonly called function.
  • How much slower is it to recreate all the layers vs. removing a particular one?
    • d3 is built on the notion of data binding. So removal of certain features is definitely something that is supported as Add layer rendering to doRedraw for the map #698 has shown.
    • Currently, we loop the layers and add each as a path. Should we consider a secondary d3 data binding approach to the layers that binds data to paths?
    • This refactor simplified a lot, so is there more overhead here than when previously calling redraw?

If there is an optimum case here, I'd rather support that than handling both cases (adding/removing layers during redraw & render).

I don't know if I have ever brought up the case of benchmarking certain features, but let's do what's more performant or more common since there seems to be no right or wrong solution.

@npmiller
Copy link

npmiller commented Dec 4, 2014

@ThomasRobertFr maybe you could comment on that.

@gordonwoodhull
Copy link
Contributor

I doubt that the logic of removing layers would create any performance penalty at all. In general, you can do as almost much computation as you want, but once you start modifying the DOM, then you have to worry about efficiency. That's why, if you are removing layers, it's much more efficient to redraw than to rerender.

Probably the loganalysis team can describe it better, but my understanding is that they are adding/removing layers without doing a full render because they have implemented semantic zoom on territories, as you can see in their demo. So in this use case, the application would have called removeLayer and then redraw, and it wouldn't have to do with whether there is crossfilter data or not.

It would be cool to use d3 data binding instead of a loop, but again I doubt there is any performance impact, and it's really a code clarity concern.

Anyway, I hope that @mtraynham and the loganalysis team will work together on this refactor for 2.1! This is the area of dc.js that I am least familiar with. (It's not supported by dcplot yet, though my colleagues have requested it.)

@mtraynham
Copy link
Contributor Author

mtraynham commented Jan 29, 2015

Oof that merge. I've updated to handle the layer logic better and satisfy #698. Since it binds the layers in now, it should automatically remove on redraw :)

mtraynham@68bbf23#diff-ff506ba8d9c0b671b44c576a6aa64674R283

Also, removed most of the unnecessary functions.

@davidcereal
Copy link

davidcereal commented Sep 8, 2016

Zoom is such a cool feature, but when using a flat-surface projection like mercator, it adds strange effects (as i believe geo.zoom intends for). Is it possible to use the new zoom to get a straight normal zoom onto a flat projection?

Thanks!

@mtraynham
Copy link
Contributor Author

mtraynham commented Sep 8, 2016

@dberger1989 It's been awhile since I've looked at the code, so it would need a bit of investigation. I do remember that some of the projections do not support rotation and that may be throwing things off. There are a few if-statements that may need to be fixed:

Is there a new d3.geo.zoom? I couldn't find one other than the examples from those sites. Davies seemingly has a newer version which looks a bit more fluent, but the source is all minified.

@mtraynham
Copy link
Contributor Author

Noticed this was getting out of date, rebased against master.

@d3netxer
Copy link

d3netxer commented Apr 4, 2018

so these changes were never pulled in?

@gordonwoodhull gordonwoodhull modified the milestones: v2.1, 3.0 Apr 4, 2018
@gordonwoodhull
Copy link
Contributor

Hi @d3netxer, that's correct.

I'm in favor of pulling this in for 3.0 but the rebase is probably going to be difficult, given that we've already ported geoChoroplethChart to d3v4 in the 3.0 branch.

I also understand that this may not be backward-compatible. May need more tests too.

So definitely not trivial, but welcome if @mtraynham and/or @kum-deepak want to take it on.

@gordonwoodhull
Copy link
Contributor

Actually probably the easier thing to do would be check if there were any changes made to the original chart and then port this PR to d3v4, replacing this chart. Then we can re-apply #1382

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants