Quick summary ↬ Gatsby recently announced the launch of “functions”. In this article, Paul Scanlon explains how to get the current location of the International Space Station (ISS) as it orbits the Earth in realtime using Gatsby Functions and then display it on a 3D interactive globe using React Three Fibre.
Gatsby recently announced the launch of Functions which opens up a new dimension of possibilities — and I for one couldn’t be more excited! With Gatsby now providing Serverless Functions on Gatsby Cloud (and Netlify also providing support via @netlify/plugin-gatsby), the framework that was once misunderstood to be “just for blogs” is now more than ever, (in my opinion) the most exciting technology provider in the Jamstack space.
The demo in this article is the result of a recent project I worked on where I needed to plot geographical locations around a 3D globe and I thought it might be fun to see if it were possible to use the same technique using off-planet locations. Spoiler alert: It’s possible! Here’s a sneak peek of what I’ll be talking about in this post, or if you prefer to jump ahead, the finished code can be found here.
With Gatsby Functions, you can create more dynamic applications using techniques typically associated with client-side applications by adding an api directory to your project and exporting a function, e.g.
The above sets up a new <Canvas /> element and can be configured using props exposed by React Three Fibre.
Elements that are returned as children of the canvas component will be displayed as part of the 3D scene. You’ll see above that I’ve included <OrbitControls /> which adds touch/mouse interactivity allowing users to rotate the scene in 3D space
Ensure ThreeScene is imported and rendered on a page somewhere in your site. In my example repo I’ve added ThreeScene to index.js:
There’s quite a lot going on in this file so I’ll walk you through it.
Create an isLoading state instance using React hooks and set it to true. This prevents React from attempting to return data I don’t yet have.
Using a useEffect I request the geojson from the CloudFront CDN.
Upon successful retrieval I set the response in React state using setGeoJson(...) and set isLoading to false
Using an Array.prototype.map I iterate over the “features” contained within the geojson response and return lineSegments with lineBasicMaterial for each geometry
I set the lineSegmentsgeometry to the return value provided by GeoJsonGeomtry which is passed the “features” geometry along with a radius of 100.
(You may have noticed I’ve used the same radius of 100 here as I’ve used in the sphereGeometryargs in three-sphere.js. You don’t have to set the radius to the same value but it makes sense to use the same radii for ThreeSphere and ThreeGeo.
Combine The Sphere And Geometry
Now it’s time to overlay the geometry on top of the blank sphere: Add ThreeGeo to ThreeScene
This function is responsible for fetching data from api.whereistheiss.at and upon success will return the data and a 200 status code back to the browser.
The Gatsby engineers have done such an amazing job at simplifying serverless functions that the above is all you really need to get going, but here’s a little more detail about what’s going on.
The function is a default export from a file named get-iss-location.js;
With Gatsby Functions the filename becomes the file path used in a client-side get request prefixed with api, e.g. /api/get-iss-location;
If the request to “Where is ISS at” is successful I return an iss_now object containing data from the Where is ISS at API and a status code of 200 back to the client;
If the request errors I send the error back to the client.
Step 3: Build The International Space Station
Creating The ISS Sphere
In this next step, I use Gatsby Functions to position a sphere that represents the International Space Station as it orbits the globe. I do this by repeatedly calling an axios.get request from a poll function and setting the response in React state.
Create a file in src/components called three-iss.js
There’s quite a lot going on in this file so I’ll walk you through it.
Create an issNow state instance using React hooks and set it to null. This prevents React from attempting to return data I don’t yet have;
The poll function is where I request the ISS location from the Gatsby Function endpoint (/api/get-iss-location);
Upon successful retrieval, I set the response in React state using setIssNow(...);
I pass the latitude and longitude onto a custom function called getVertex, along with a radius.
You may have noticed that here I’m using a radius of 120. This does differ from the 100 radius value used in ThreeSphere and ThreeGeo. The effect of the larger radius is to position the ISS higher up in the 3D scene, rather than at ground level — because that’s logically where the ISS would be, right? 100 has the effect of the sphere and geometry overlapping to represent Earth, and 120 for the ISS has the effect of the space station “orbiting” the globe I’ve created.
One thing that took a bit of figuring out, at least for me, was how to use spherical two dimensional coordinates (latitude and longitude) in three dimensions, e.g. x,y,z. The concept has been explained rather well in this post by Mike Bostock.
The key to plotting lat / lng in 3D space lies within this formula… which makes absolutely no sense to me!
Luckily, Three.js has a set of MathUtils which I’ve used like this:
Pass the latitude, longitude and radius into the getVertex(...) function
Create a new THREE.Spherical object from the above named parameters
Set the THREE.Vector3 object using the Spherical values returned by the setFromSpherical helper function.
These numbers can now be used to position elements in 3D space on their respective x, y, z axis — phew! Thanks, Three.js!
Et voilà! You should now be looking at something similar to the image below.
The poll function will repeatedly call the Gatsby Function, which in turn requests the current location of the ISS and re-renders the React component each time a response is successful. You’ll have to watch carefully but the ISS will change position ever so slightly every 5 seconds.
The ISS is traveling at roughly 28,000 km/h and polling the Gatsby Function less often would reveal larger jumps in position. I’ve used 5 seconds here because that’s the most frequent request time as allowed by the Where is ISS at API
You might have also noticed that there’s no authentication required to request data from the Where is ISS at API. Meaning that yes, technically, I could have called the API straight from the browser, however, I’ve decided to make this API call server side using Gatsby Functions for two reasons:
It wouldn’t have made a very good blog post about Gatsby Functions if i didn’t use them.
Who knows what the future holds for Where is ISS at, it might at some point require authentication and adding API keys to server side API requests is pretty straightforward, moreover this change wouldn’t require any updates to the client side code.
Step 4: Make It Fancier! (Optional)
I’ve used the above approach to create this slightly more snazzy implementation: https://whereisiss.gatsbyjs.io,
In this site I’ve visualized the time delay from the poll function by implementing an Svg <circle /> countdown animation and added an extra <circle /> with a stroke-dashoffset to create the dashed lines surrounding it.
Step 5: Apply Your New Geo Rendering Skills In Other Fun Ways!
I recently used this approach for plotting geographical locations for the competition winners of 500 Bottles: https://500bottles.gatsbyjs.io. A limited edition FREE swag giveaway I worked on with Gatsby’s marketing team.
You can read all about how this site was made on the Gatsby blog: How We Made the Gatsby 500 Bottles Giveaway
In the 500 Bottles site I plot the geographical locations of each of the competition winners using the same method as described in ThreeIss, which allows anyone visiting the site to see where in the world the winners are.
Gatsby Functions really open up a lot of possibilities for Jamstack developers and never having to worry about spinning up or scaling a server removes so many problems leaving us free to think about new ways they can be used.
I have a number of ideas I’d like to explore using the V4 Space X API’s so give me a follow if that’s your cup of tea: @PaulieScanlon