Last time we implemented a basic addTwo function in C++ to call from Node. This time we’ll go one step further. Let’s make a function distanceToLondon to take the coordinates anywhere on Earth and find out how far away we are from tea and crumpets.

Complete code for this exercise is here

Setup

Starting from part 1, your directory structure should look like this.

cbrown@Chriss-MacBook-Air performant-node % tree
.
├── binding.gyp
├── index.js
├── package-lock.json
├── package.json
└── src
    └── main.cpp

If you’ve built the project, you’ll also have a node_modules and a build directory.

Now make sure you have the Boost library installed if you don’t have it already. On Mac with brew you can just use:

brew install boost

If you encounter compatibility issues make sure you get Boost version 1.85, to match what we use here.

Code

First of all, let’s rename main.cpp to app.cpp to make it more clear what we’re building (and also update binding.gyp to use the new name).

mv src/main.cpp src/app.cpp && sed -i '' 's/main.cpp/app.cpp/g' binding.gyp

Now, time to update our repo with the changes we need.

building.gyp

  • Force valid exception handling using the -fexceptions flag (the above config forces on Linux, Mac, and Windows)
  • Add Boost version 1.85.0 to the include directories. The libraries we use are .hpp files so we just need to include the header and don’t need to worry about linking

app.cpp

  • Update our module to export distanceToLondon
  • Use the input validation from the previous part to ensure we get two numbers (longitude, latitude)
  • Create a point on the Earth for the input coordinates, and also one for London
  • Call boost::geometry::distance to calculate the distance in meters, then divide by 1000 to get kilometers Our new function takes in two numbers and returns a number, same as before. So other than including in the new library, not much has changed 🙂

index.js

  • Update index.js to call our new function. Here we use New York City but use anything you like.

Code

building.gyp

{
  "targets": [
    {
      "target_name": "addon",
      "cflags": [
        "-fexceptions"
      ],
      "cflags_cc": [
        "-fexceptions"
      ],
      "sources": [
        "./src/app.cpp"
      ],
      "include_dirs": [
        "<!(node -p \"require('node-addon-api').include_dir\")",
        "/opt/homebrew/Cellar/boost/1.85.0/include"
      ],
      "dependencies": [
        "<!(node -p \"require('node-addon-api').gyp\")"
      ],
      "xcode_settings": {
        "OTHER_CFLAGS": [
          "-fexceptions"
        ],
        "OTHER_CPLUSPLUSFLAGS": [
          "-fexceptions"
        ]
      },
      "msvs_settings": {
        "VCCLCompilerTool": {
          "ExceptionHandling": 1
        }
      }
    }
  ]
}

src/app.cpp

#include <napi.h>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point.hpp>

Napi::Number DistanceToLondon(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();

    double lon = info[0].As<Napi::Number>().DoubleValue();
    double lat = info[1].As<Napi::Number>().DoubleValue();

    boost::geometry::model::point<double, 2, boost::geometry::cs::geographic<boost::geometry::degree>> point1(lon, lat);
    boost::geometry::model::point<double, 2, boost::geometry::cs::geographic<boost::geometry::degree>> london(-0.1276, 51.5074);

    double distance = boost::geometry::distance(point1, london) / 1000; // distance in km

    return Napi::Number::New(env, distance);
}

Napi::Object Init(Napi::Env env, Napi::Object exports) {
    exports.Set(Napi::String::New(env, "distanceToLondon"), Napi::Function::New(env, DistanceToLondon));
    return exports;
}

NODE_API_MODULE(addon, Init)

index.js

const addon = require("./build/Release/addon");

const newYorkLongitude = -74.006;
const newYorkLatitude = 40.7128;

const nycToLondonKm = addon.distanceToLondon(newYorkLongitude, newYorkLatitude);
console.log(`Distance from NYC to London: ${nycToLondonKm.toFixed(2)} km`);

Demonstration

Now at this point, you can run your new function using

npx node-gyp && npx node-gyp configure build && node index.js

Output:

cbrown@Chriss-MacBook-Air performant-node2 % npx node-gyp && npx node-gyp configure build && node index.js
...
Distance from NYC to London: 5585.34 km

Exactly the answer we are hoping for.

Conclusion

We exposed a C++ function to Node that calculates the distance between two points on the globe. Boost provides an efficient implementation of Vincenty’s formula for determining the distance between two coordinates on Earth. In a real application, you would want to benchmark how much faster the C++ function is than anything you could find or write in Node.

Extension Ideas

  • Update index.js to take in the name of a city, call an API to determine its coordinates, perform the distance calculation then output a nice formatted result
  • Compare accuracy and runtime for both Vincenty and Haverson algorithms
  • Compare speed when delegating distance calculation to the Boost library vs a relevant npm library

Up next

Server-side programming is the beating heart of the average web app, but we can’t neglect what the client is doing. Coming up next, we’ll learn how to use web assembly to integrate C++ with JS on the client side.