How to return a map / SVG to Observable?

I am looking for a function that would allow me to ‘preview’ any json map file – as is done when you upload a geoJSON file to GitHub. TheD3 mapping tutorial y MIT’s DUSPviz looks like a good lead, as the code it uses to render a map to HTML is super short:


<script>
    var width = 700;
    var height = 580;

    var svg = d3.select( "body" )
        .append( "svg" )
        .attr( "width", width )
        .attr( "height", height );

    var g = svg.append( "g" );

    var albersProjection = d3.geoAlbers()
        .scale( 190000 )
        .rotate( [71.057,0] )
        .center( [0, 42.313] )
        .translate( [width/2,height/2] );

    var geoPath = d3.geoPath()
        .projection( albersProjection );

    g.selectAll( "path" )
        .data( neighborhoods_json.features )
        .enter()
        .append( "path" )
        .attr( "fill", "#ccc" )
        .attr( "stroke", "#333")
        .attr( "d", geoPath );
    </script>

However, in Observable this doesn’t render out the map:

I’v tried adding elements I’ve seen from other examples, like yeild svg and return svg.node();, but I can’t seem to get the map to draw.

What is the missing step?

I appreciate the insights!

So silly - it was right in front of me!

The d3 function is saying to select the html body. I can’t do that in observable. I can select a <div> element. Changing that statement gets it working! :slight_smile:

Getting closer, but my dream of simply taking some code and pointing to a JSON file alludes me. I tried using the MIT code to show another dataset, but still haven’t worked through all the bugs to make it show:

A while ago I got these same data working from another online example:

Alas! I’ll search around for some more code for a GitHub-like previewer :frowning:

Hi Aaron,

I had a look at your notebook and tweaked it / tried to give some comments here:

It’s still rather far from being a completely general solution but I hope the comments help you work out bugs in the future.

In my experience, when trying to get random JS code to work in Observable, I’ve always had to get a good understanding of how it works when running outside of Observable. The tutorial you linked looks good but there are a few things that may have tripped you up:

First, they load their data in a different way by wrapping their JSON object in JS and placing it in a script tag; if you want to get files from the web, you’ll have to use d3-json or something similar.

Second, their data is a geoJSON file and the file in your notebook is a topoJSON file. I can’t say I’ve studied either of these formats, but the differences seem significant. In any case, in your notebook we needed to import the topojson library as well.

1 Like

There are a lot of example maps in the Maps collection. To highlight two:

To echo what @bgchen said, the most important bit of advice on Observable is that you should avoid selecting from the DOM (document.querySelector, document.querySelectorAll, etc.). Instead, each cell should return its value “from scratch”, creating and returning new elements.

If you want to use D3 to create the DOM, use d3.create to create a detached element and select it:

const svg = d3.create("svg")
    .attr("width", width)
    .attr("height", height);

Or with DOM.svg:

const svg = d3.select(DOM.svg(width, height));

Personally, I prefer to use HTML template literals since it’s more declarative, and I only use D3 if I need to animated transitions or something fancy like that. (See D3 Without Joins for more.)

2 Likes

Thanks to you both! @bgchen - I especially appreciate the lengths you take to teach; big :heart: for your commented example!

@mbostock – Thanks for the many examples and tutorials. With regard to the examples you linked, and as a general observation on the many map examples on this website-- I have found it easy to play with an manipulate examples where with a global view (like the ones you link), or adding and playing with data on maps that are already made (like ones with US states), but I have a much harder time trying to figure out how to load and render some random map that I find on the Internet (such as the map of Indonesia that I was playing around with).

What I like about the GitHub previewer is that if I find (or make) a geoJSON or topoJSON file and save it to GitHub, that files appears in the viewer. I don’t have to know anything about the file – where it’s located, what view level, etc. I realize there’s a lot of magic behind this, but I am hoping that, at its heart, there’s not much to the process of rendering a JSON file… that there a small, reusable bit of code out there to which I can ‘connect’ a geoJSON or topoJSON file and have it ‘appear’, and then to it I can start adding data. In the examples you linked, unfortunately, I can’t just switch-out the data and have the map automatically adjust. While I can user Tom’s Leaflet notebook pretty easily, I still clumsily have to mess around with the viewing bounds and also have all the overhead of rendering my data on top of the leaflet map (which is exactly what GitHub does for me).

I am inspired by your streaming shapefiles example but here too I’ve struggled with the same sorts of issues: if I can’t figure out the correct projection of my shapefile data, and if I don’t know the coordinates for the viewing bounds, I can’t get it to load or display correctly. I therefore find myself importing a shapefile or json file to Google Earth (or GitHub) to figure out where and how it will look before I even try adding it to Observable.

I am slowly learning (thanks to you!) but it’s been a real challenge for me to try to figure out how to get the building blocks in place for working with maps and data in JavaScript (or anything, for that matter). I keep trying because I feel that I am making progress and because of your tremendous help and encouragement, but the barriers to entry still seem pretty high.

At the end of the day, my goal is to be able to ask things like: where are the weather stations near to this area, and what do they tell us about air quality? Or how far are people’s homes in this city from a hospital, on average? As I research these questions, so much comes back to D3.js and as I think about how to communicate the answers I find in a way that is transparent and reproducible, it seems that JavaScript is the way to go. Just wish there were a few more examples and tutorials out there of how to start from the very basics. You’re doing a lot in that respect - and I can’t thank you enough!

@aaronkyle Clearly, there’s a lot to learn, depending on your interests.

Regarding GeoJSON, most viewers assume that the geometry is planar (projected), with the y-axis pointing up (North) rather than down as it is by default in most graphics coordinate systems, such as Canvas and SVG. You can use d3.geoIdentity to render planar GeoJSON. Here’s an example using my reusable file input:

TopoJSON, in contrast, can be either spherical (unprojected) or planar (projected), and when it’s projected, the y-axis typically points down.

Of course, mapshaper.org is a better for quick previewing of GeoJSON, or Shapefiles, or other things, and supports interactive pan and zoom and format conversion. Those could be added to the above notebook, though, if you were so motivated.

1 Like

woah - cool! Thanks for moving this forward!

Thanks, too, for the link to mapshaper.org . Looks straight-forward. I’ll see if I can learn how do implement interactive elements from from other examples.

I also found Tom’s list: GeoJSON utilities that will make your life easier., which links to many other goodies.

It blows my mind how much work and writing the Observable community has done!

It’s pretty awesome how Mike’s GeoJSON viewer very quickly renders files that exceed the sizes allowed with the GitHub viewer.

And on this same general path of exploration, look how beautifully this was done only yesterday by @sugi2000 !

Apologies for missing this earlier. The GitHub repository has one or two more beautiful renderings.

I see that all this work links back to Mike, in this case the U.S. Atlas TopoJSON repository. All of this is so incredibly generous and inspiring. :heart:

1 Like