Skip to content

expose the projection to make it reusable #1191

Open
@Fil

Description

@Fil
Contributor

maybe just as plot.scale("projection")? If the goal is to make it reusable, it doesn't matter if it's an opaque {stream}, and the code is ultra-simple. If we extend the scope and want to get the name back, it's much more difficult.

Activity

added
enhancementNew feature or request
questionFurther information is needed
geoMaps and projections
on Dec 16, 2022
jheer

jheer commented on Mar 21, 2024

@jheer

Exposing projection information would be very useful to Mosaic vgplot. We could then support brushing/filtering over explicitly projected data, at least initially for planar projections and those with separable lon -> x, lat -> y mappings (like unrotated mercator or equirectangular).

I have a working prototype of this locally, which extends Plot's projection.js like so:

return {
  width: dx,
  height: dy,
  offset: [marginLeft + insetLeft, marginTop + insetTop],
  translate: [tx, ty],
  scale: k,
  invert: projection.invert, // may be undefined
  stream: (s) => projection.stream(transform.stream(clip(s)))
};

One also needs to first define let k = 1 in the outer scope. I'm exporting dimension information (width, height, offset) to infer the equivalent of scale ranges and to aid setting the boundaries of the brushable region. I use the (translate, scale) data to create my own invert methods (including optimized 1D variants for planar projections). Alternatively, an end-to-end invert method that takes any affine transform into account would work fine.

The only other change is to modify plot.js to expose the projection:

figure.projection = () => context.projection;

I assume you would want to run this through an exposeProjection method that makes a defensive copy.

Not sure how well something like the above would fit in with your plans and sensibilities, but I can make a PR if it does.

Fil

Fil commented on Mar 21, 2024

@Fil
ContributorAuthor

Not to say that we should absolutely support every funky projection out there, but I'm weary of an approach that by design wouldn't allow to work, for example, with polar projections, or maps rotated to include the antimeridian?

Have you thought of going in the opposite direction? If mosaic had only access to the stream — or maybe a convenience method projection(lon, lat) => [x, y] returned by Plot, could it initialize its brushing component by computing all the screen positions (once); these derived dimensions would be added to the datacube, and brushing would work normally. This would make no assumption on what the projection returns.

The drawback of this approach —and I guess that's what you're trying to avoid?— is that you have to compute projected values [x,y] for the whole dataset. But since brushing does not require an extreme precision (that is, a precision of 1 pixel or 0.5 pixel is enough), there might be ways to optimize this even when you have a bazillion data points, by binning or sampling?

Happy to explore this with you in any case—the more maps we can make, the 👍

jheer

jheer commented on Mar 21, 2024

@jheer

Yes, there are plenty of non-invertible projections that would be nice to support. As you note, we can compute the projection as a pre-processing step, then rely on the projected (x, y) values. We do this in-database for examples like the Gaia star map and NYC taxis. In the Mosaic context, we need these projected coordinates to reside in-database for scalable and interoperable filtering.

However, all that resides outside of Plot. So for cases where we want to use Plot projections (and their nice defaults, scaling, etc) we currently have a bit of a chicken-and-egg problem. So I've been exploring cases where we can create meaningful selections over lon/lat via projection inversion. If ultimately this proves to be a dead-end, so be it!

In any case it would be nice to be able to access the projection - even if just the stream!

linked a pull request that will close this issue on Mar 26, 2024
jaanli

jaanli commented on Apr 10, 2024

@jaanli
Fil

Fil commented on Apr 10, 2024

@Fil
Author
jaanli

jaanli commented on Apr 10, 2024

@jaanli
jaanli

jaanli commented on Apr 10, 2024

@jaanli

Would also want to use this to enable filtering for data like this: https://jaanli.github.io/new-york-real-estate between Mosaic, Plot, and Protomaps / Maplibre GL types of visualizations like this...

tomlarkworthy

tomlarkworthy commented on Jan 13, 2025

@tomlarkworthy

Is there a workaround to get the [lat, long] => [px, py] transform when there is a projection?

tomlarkworthy

tomlarkworthy commented on Jan 15, 2025

@tomlarkworthy

Hi I was watching this issue and I see you want to gather more use cases. Maybe its similar to jhaar's but I want to enable manipulation of the plot in data space.

https://observablehq.com/@tomlarkworthy/manipulate#plot_location

For that I need to invert a pixel-space delta to a data-space delta. For categorical scales I brute force. I was expecting that for projections I would apply some kind of iterative hill climbing to the well-defined pixel -> data function. I think trying to provide an invert is maybe too opinionated to do because it's an ill defined problem (some projections have multiple solutions for the invert).

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestgeoMaps and projectionsquestionFurther information is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @Fil@jheer@tomlarkworthy@jaanli

      Issue actions

        expose the projection to make it reusable · Issue #1191 · observablehq/plot