rveciana / d3-composite-projections

Set of d3 projections for showing countries distant lands together
http://rveciana.github.io/d3-composite-projections/
Other
99 stars 22 forks source link

Is there a way to show Puerto Rico in albersUSA projection? #22

Open armsp opened 4 years ago

armsp commented 4 years ago

I can see that you were able to show Puerto Rico in albersUsa projection. I am trying to do the same. I have a file that has Puerto Rico and want to render it in albersUsa projection but it gets filtered out - vanishes every time I use albersUSA projection.

Is there a way to render it by Vega or Vega-Lite in "albersUsa" projection without filtering Puerto Rico. Any leads would be very helpful.

rveciana commented 4 years ago

I think that the examples in d3js page have Puerto Rico. Also, the example in this library us albers territories have all Puerto Rico counties too.

El ds., 27 juny 2020, 20.22, Shantam Raj notifications@github.com va escriure:

I am trying to get a geojson file that would have Puerto Rico along with all the US counties in a composite albersUSA projection. So that the file would be rendered by Vega or Vega-Lite in "albersUsa" projection without filtering Puerto Rico.

Is there a way to do that. Any leads would be very helpful.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rveciana/d3-composite-projections/issues/22, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAVTT5N66A62YBL7PYJF2P3RYY2HJANCNFSM4OKEPS4Q .

JulesBlm commented 3 years ago

Take a look at the following Observable notebook D3: U.S. Map with Puerto Rico . I'm not sure how to go about it in Vega though

armsp commented 3 years ago

@JulesBlm That was my first stop. But that's just an example. The issue is that in the map included by D3 internally for the albersUSA projection, they don't use Puerto Rico. Many people have asked for it but I still don't know why it's not supported. Because Vega uses D3 internally, the maps get carried over. Even I have not figured out how yet how to do it.

JulesBlm commented 3 years ago

@armsp Ok you made me curious so I fiddled around with it for a while, and good news, it is possible in Vega. I just made this Observable notebook that uses the custom US Puerto Rico projection of the previously mentioned notebook. Apparently, any custom D3 projection can be used with Vega [source], this is also briefly mentioned in the Vega docs.

armsp commented 3 years ago

@JulesBlm That is brilliant. I am now trying to figure out how to make Vega-Lite use that custom projection. Because it is still not using a custom projection. For my preliminary tests I edited your Notebook and added the following -

vl.markGeoshape()
  .data(vl.topojson('data/us-10m.json').feature('counties'))
  .project(vl.projection('geoAlbersUsaPr'))
.width(900).height(500)
  .render()

But unfortunately that just shows a blank output.

I wish I was well versed with JS and GIS like you, but I only know Python. If you figure something out with Vega-Lite then do let me know, cause that is my ultimate goal. Vega was just the starting point.

armsp commented 3 years ago

@JulesBlm would you have any idea on how to make this custom projection be called from a Vega Spec? I tried all day using your projection string in a Vega JSON Spec and visualizing the output...but all I get is a blank map.

Would you also happen to know how to actually use that projection in an actual map?

JulesBlm commented 3 years ago

@armsp If you could share your attempt in an Observable notebook or code block here I could take a look. Right now it's really hard to tell for me why your approach does not work. Also note that I import the custom projection (function geoAlbersUsaPr() {...} from the other notebook, it's not just about the string of the custom projection. My tip would be to look carefully at the error messages that come up.

armsp commented 3 years ago

@JulesBlm I lost my original Notebook with a lot more experiments....from whatever I could remember I made this one that you can check if it is of any help getting started with Vega-Lite - https://observablehq.com/d/87bbae6f7ca887a6

Unfortunately I am a beginner with JS so that is what I could come up with. And this unfamiliarity has been the bottleneck in getting a custom projection to register in Vega and also be available to be used in Vega-Lite.

JulesBlm commented 3 years ago

Ok I made some suggestions to that notebook over on Observable. I'll copy my comments here for good measure: You can use a custom projection with Vega Embed as Vega Embed just requires a Vega object. To make use of a custom projection, you have to register it first with Vega, only then you can use the custom projection string (geoAlbersUsaPr). So if you plan to use this on webpage be sure to include the entire custom projection

import { geoConicEqualArea, geoAlbers } from "d3-geo"
import VegaEmbed from 'vega-embed';
// import your us map here too

// Copy the following functions from https://observablehq.com/@d3/u-s-map-with-puerto-rico
function geoAlbersUsaPr() {
 ...
// be sure to change d3.geoAlbers() to geoAlbers(), and d3.geoConicEqualArea() to geoConicEqualArea() if you're importing only those two functions from d3-geo
}

function multiplex(streams) {
  const n = streams.length;
  return {
    point(x, y) { for (const s of streams) s.point(x, y); },
    sphere() { for (const s of streams) s.sphere(); },
    lineStart() { for (const s of streams) s.lineStart(); },
    lineEnd() { for (const s of streams) s.lineEnd(); },
    polygonStart() { for (const s of streams) s.polygonStart(); },
    polygonEnd() { for (const s of streams) s.polygonEnd(); }
  };
}

const epsilon = 1e-6

// register the custom projection defined above with string "geoAlbersUsaPr"
vega.projection('geoAlbersUsaPr', geoAlbersUsaPr);

// Make your Vega specification with the custom projection string defined above
const vegaUSAwithPuertoRico = ({
  "$schema": "https://vega.github.io/schema/vega/v3.json",
  "width": 900,
  "height": 500,
  "autosize": "none",

  "data": [
    {
      "name": "states",
      "values": us,
      "format": {
        "type": "topojson",
        "feature": "states"
      }
    },    
    {
      "name": "counties",
      "values": us,
      "format": {
        "type": "topojson",
        "feature": "counties"
      }
    }
  ],
  "projections": [
    {
      "name": "projection",
      "type": "geoAlbersUsaPr"
    }
  ],

  "marks": [
    {
      "type": "shape",
      "from": {"data": "counties"},
      "encode": {
        "update": {
          "stroke": {"value": "#aaa"},
          "strokeWidth": {"value": "0.5"}
        }
      },
      "transform": [
        { "type": "geoshape", "projection": "projection" }
      ]
    },
    {
      "type": "shape",
      "from": {"data": "states"},
      "encode": {
        "update": {
          "stroke": {"value": "#000"},
          "strokeWidth": {"value": "1"}
        }
      },
      "transform": [
        { "type": "geoshape", "projection": "projection" }
      ]
    } 
  ]
})

// Then plot this Vega specification VegaEmbed (you can also Vega plot)
VegaEmbed(vegaUSAwithPuertoRico)

I'm fairly sure custom projections are not possible in Vega-Lite. Vega-lite is simpler and therefore allows less customization.

armsp commented 3 years ago

@JulesBlm I think I am wrapping my head around it....the thing is Vega-Lite specifications COMPILE to Vega before they are rendered or plotted. This usually happens internally.

So now when I write the Vega-Lite spec (with our custom projection string) and use the inbuilt compile function to get the compiled Vega spec and then plot it - this should work theoretically right?

But so far I have been unsuccessful. Let me see if I can make another notebook for this. I am absolutely new to Observable, so please pardon my lack of experience and methods.

EDIT This notebook has my experiments with the compiled vega from Vega Lite - https://observablehq.com/d/87bbae6f7ca887a6