eval d3.js projection from a string?

I don’t know if it’s possible, but I don’t understand why eval(“d3.geoStereographic()”) returns the message “ReferenceError: d3 is not defined”. Why is d3 not defined? Is there any other way to evaluate this string?

In observable you mean? you’d need to define the d3 reference in the same cell:

{d3; return eval("d3.geoStereographic()")}

Thanks Fil

And outside Observable?
Actually, I try to find a solution to run bertin.js with a serialized config file.
See Untitled / neocarto / Observable
But, I’m not sure what relevant method to use to retrieve the projection and the geojson layers. Any help is welcome.

try this:

config = {
  d3;

  let json = JSON.parse(str);

  //  replace projection
  if (json.params.projection.substring(0, 6) === "d3.geo") {
    const projection = eval(json.params.projection);
    if (typeof projection !== "function") {
      throw new Error(`projection ${json.params.projection} is not available`);
    }
    json.params.projection = projection
  }

  // replace geojson

  //json.layers[0].geojson = eval(json.layers[0].geojson);
  json.layers[0].geojson = world;

  return json;
}

Perfect. Thank you very much.

Well… It’s me again. Actually, I don’t really understand how all of this works.

If I write an ES6 module like this :

import * as d3selection from "d3-selection";
import * as d3geo from "d3-geo";
import * as d3geoprojection from "d3-geo-projection";
const d3 = Object.assign({}, d3selection, d3geo, d3geoprojection);

export function test(str) {
  d3;
  const fn = eval(str);
  return fn;
}

It does not work anymore.

If I try to execute

test("d3.geoBertin1953()")

an error is returned : TypeError: d3.geoBertin1953 is not a function

But why? :person_shrugging:

Mentioning d3 in a cell is specific to Observable’s runtime.

Also, please read https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#never_use_eval!
maybe you’ll find the right way to do this with window.Function instead? (Note that in some environments even the use of Function is discouraged.)

1 Like

Ok. Thank you. I think I’ll give up my (bad) idea.

I recommend to use dependency injection via a wrapper:

// function anonymous(d3) { return (d3.geoBertin1953()) }
createProjection = new Function('d3', `return (${json.params.projection})`)

This produces the function

// createProjection.toString()
function anonymous(d3
) {
return (d3.geoBertin1953())
}

which you can then invoke to pass in d3:

projection = createProjection(d3)
3 Likes

Great!