Skip to content
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

Add .contain() method? #49

Closed
veltman opened this issue Jul 11, 2016 · 11 comments
Closed

Add .contain() method? #49

veltman opened this issue Jul 11, 2016 · 11 comments

Comments

@veltman
Copy link
Contributor

veltman commented Jul 11, 2016

Any interest in adding a projection.contain() method that projects to a bounding box a la http://bl.ocks.org/mbostock/4707858?

I think it's a pretty common use case to start with a bounding box rather than a desired scale/translate, and the alternative is to fiddle with values until it looks nice.

I took a crack at it, mostly seems to work except I need to figure out why it doesn't work for projections with clipExtents:

https://github.com/veltman/d3-geo/blob/master/src/projection/index.js#L81-100

In terms of naming, I chose contain because that's the CSS background-size nomenclature, but it could also be something like fitTo.

This would break the getter/setter pattern a bit. Alternatively, it could be a more consistent separate method like:

  var projection = ...;

  var fitter = d3.geoFit()
    .projection(projection)
    .width(1280)
    .height(720)
    .scale(0.9);

  // Change
  fitter.scale(1);

Although that seems kind of verbose. Any thoughts?

@mbostock
Copy link
Member

Yes, I think this is a great idea. I filed this as d3/d3-geo-projection#30 previously but that was back when I was planning on keeping the projection-related code in that module (which won’t be possible until the new geo pipeline is implemented for 5.0), so anyway, it’s fine to move that issue over here.

That issue has my proposed API: path.fit(feature, extent). But I haven’t thought about it too much so it might need redesigning.

@veltman
Copy link
Contributor Author

veltman commented Jul 12, 2016

Oh great, I hadn't seen that one. It seems to me like it would be a little more intuitive to attach it to the projection rather than the path since the projection's what's getting modified. How about something like:

projection.fit(feature, extent[, padding])

Where feature is the geometry, extent is [width, height], and padding is an optional percentage padding a la the old rangeBands?

@mbostock
Copy link
Member

You’ll need path.bounds to compute the bounding box, and potentially the
results could be affected by path.pointRadius although I don’t think that’s
the case for the current implementation of path.bounds.

Perhaps path.area, path.bounds and path.centroid should move to the
projection instance. I don’t think these need to be on a path. (Though we’d
need to retain them until 5.0 for backwards-compatibility.)
On Tue, Jul 12, 2016 at 8:49 AM Noah Veltman notifications@github.com
wrote:

Oh great, I hadn't seen that one. It seems to me like it would be a little
more intuitive to attach it to the projection rather than the path since
the projection's what's getting modified. How about something like:

projection.fit(feature, extent[, padding])

Where feature is the geometry, extent is [width, height], and padding is
an optional percentage padding a la the old rangeBands?


You are receiving this because you commented.

Reply to this email directly, view it on GitHub
#49 (comment), or mute
the thread
https://github.com/notifications/unsubscribe/AAOEjXHCUJu_Zk4IIgNOFk99ELzCU5tMks5qU41hgaJpZM4JJ4Qx
.

@mbostock
Copy link
Member

mbostock commented Jul 12, 2016 via email

@veltman
Copy link
Contributor Author

veltman commented Jul 12, 2016

A separate geoExtent or geoFit object definitely feels like overkill. Skipping the padding works for me. Will probably lead to lots of code along the lines of:

projection.fit(feature, [[margin.left, margin.top], [width - margin.right, height - margin.bottom]])

In cases where the container hadn't been pre-translated. Not the neatest but it's nice and explicit and seems particularly appropriate for a canvas context or insets/small multiples.

Moving path.area, path.bounds, and path.centroid to projection in 5.0 makes sense to me, if none of them are affected by any path-specific options. I only wonder whether having both projection.fit for fitting and projection.bounds for calculating bounds might be a little confusing.

@mbostock
Copy link
Member

We can move the methods in 4.2 and leave the old ones as deprecated aliases
for backwards compatibility.
On Tue, Jul 12, 2016 at 12:40 PM Noah Veltman notifications@github.com
wrote:

A separate geoExtent or geoFit object definitely feels like overkill.
Skipping the padding works for me. Will probably lead to lots of code along
the lines of:

projection.fit(feature, [[margin.left, margin.top], [width - margin.right,
height - margin.bottom]])

In cases where the container hadn't been pre-translated. Not the neatest
but it's nice and explicit and seems particularly appropriate for a canvas
context or insets/small multiples.

Moving path.area, path.bounds, and path.centroid to projection in 5.0
makes sense to me, if none of them are affected by any path-specific
options. I only wonder whether having projection.fit for fitting and
projection.bounds for calculating bounds might be a little confusing.


You are receiving this because you commented.

Reply to this email directly, view it on GitHub
#49 (comment), or mute
the thread
https://github.com/notifications/unsubscribe/AAOEjTAFHFyISLhjKDyPlDKNJriGQZpFks5qU8N4gaJpZM4JJ4Qx
.

@mbostock
Copy link
Member

A related consideration here is whether projection.fit should set
projection.center or projection.translate, or both. (In addition to setting
projection.scale.)

I guess it should set only projection.translate, but I did find it useful
to set a default projection.center in some of the extended projections to
make it easier to rescale and recenter them without having to compute a
complicated translate.

But in general it might be hard to set projection.center to the unprotected
point at the center of the projected feature’s bounds, since it could be
undefined if you have a weird projection like Hammer retroazimuthal.
On Tue, Jul 12, 2016 at 12:55 PM Mike Bostock mike@ocks.org wrote:

We can move the methods in 4.2 and leave the old ones as deprecated
aliases for backwards compatibility.
On Tue, Jul 12, 2016 at 12:40 PM Noah Veltman notifications@github.com
wrote:

A separate geoExtent or geoFit object definitely feels like overkill.
Skipping the padding works for me. Will probably lead to lots of code along
the lines of:

projection.fit(feature, [[margin.left, margin.top], [width -
margin.right, height - margin.bottom]])

In cases where the container hadn't been pre-translated. Not the neatest
but it's nice and explicit and seems particularly appropriate for a canvas
context or insets/small multiples.

Moving path.area, path.bounds, and path.centroid to projection in 5.0
makes sense to me, if none of them are affected by any path-specific
options. I only wonder whether having projection.fit for fitting and
projection.bounds for calculating bounds might be a little confusing.


You are receiving this because you commented.

Reply to this email directly, view it on GitHub
#49 (comment), or mute
the thread
https://github.com/notifications/unsubscribe/AAOEjTAFHFyISLhjKDyPlDKNJriGQZpFks5qU8N4gaJpZM4JJ4Qx
.

@mbostock
Copy link
Member

Actually I forgot that path.area and related methods are sometimes used
outside of projections, as with d3.geoTransform. So it's fine to have
projection.fit but I wouldn't move the other methods.
On Tue, Jul 12, 2016 at 1:03 PM Mike Bostock mike@ocks.org wrote:

A related consideration here is whether projection.fit should set
projection.center or projection.translate, or both. (In addition to setting
projection.scale.)

I guess it should set only projection.translate, but I did find it useful
to set a default projection.center in some of the extended projections to
make it easier to rescale and recenter them without having to compute a
complicated translate.

But in general it might be hard to set projection.center to the
unprotected point at the center of the projected feature’s bounds, since it
could be undefined if you have a weird projection like Hammer
retroazimuthal.
On Tue, Jul 12, 2016 at 12:55 PM Mike Bostock mike@ocks.org wrote:

We can move the methods in 4.2 and leave the old ones as deprecated
aliases for backwards compatibility.
On Tue, Jul 12, 2016 at 12:40 PM Noah Veltman notifications@github.com
wrote:

A separate geoExtent or geoFit object definitely feels like overkill.
Skipping the padding works for me. Will probably lead to lots of code along
the lines of:

projection.fit(feature, [[margin.left, margin.top], [width -
margin.right, height - margin.bottom]])

In cases where the container hadn't been pre-translated. Not the neatest
but it's nice and explicit and seems particularly appropriate for a canvas
context or insets/small multiples.

Moving path.area, path.bounds, and path.centroid to projection in 5.0
makes sense to me, if none of them are affected by any path-specific
options. I only wonder whether having projection.fit for fitting and
projection.bounds for calculating bounds might be a little confusing.


You are receiving this because you commented.

Reply to this email directly, view it on GitHub
#49 (comment), or mute
the thread
https://github.com/notifications/unsubscribe/AAOEjTAFHFyISLhjKDyPlDKNJriGQZpFks5qU8N4gaJpZM4JJ4Qx
.

@veltman
Copy link
Contributor Author

veltman commented Jul 12, 2016

One thing to note, it seems like that project to bounding box approach goes haywire with a gnomonic projection:

https://gist.github.com/veltman/a1d532a697764f05d0093dec40c571b2

@mbostock
Copy link
Member

Yes. Many projections extend to infinity if you’re not careful.
On Tue, Jul 12, 2016 at 6:36 PM Noah Veltman notifications@github.com
wrote:

One thing to note, it seems like that project to bounding box approach
goes haywire with a gnomonic projection:

https://gist.github.com/veltman/a1d532a697764f05d0093dec40c571b2


You are receiving this because you commented.

Reply to this email directly, view it on GitHub
#49 (comment), or mute
the thread
https://github.com/notifications/unsubscribe/AAOEjRvYXVUdRzfmHher7g9awMVBBVSwks5qVBbbgaJpZM4JJ4Qx
.

@mbostock
Copy link
Member

Fixed in #50 by @veltman! 🎉

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

No branches or pull requests

2 participants