How to Build a Coach Holiday Showcase with WRLD

This article was created in partnership with WRLD. Thank you for supporting the partners who make SitePoint possible.

Since the late 2000s, maps have been a staple of the web, and today are more ubiquitous than ever. They have a variety of uses, from complementing a company’s contact page to plotting places of interest across a geographic region. As computing power is becoming more affordable, 3D maps have gained popularity, demonstrating the scale of buildings and the nature of the surrounding terrain, adding a touch of novelty to such experiences, as well as providing practical benefits.

One such provider of 3D maps is WRLD. Major cities such as New York, London, Paris and Bangkok have been generated in 3D using real geographic data. This data can be used to author custom maps containing points of interest and other useful metadata, and thanks to their Leaflet-driven JavaScript library, it’s possible to drop them into a web page with a few lines of code; this workflow will be the focus of our article.

--ADVERTISEMENT--

Using WRLD’s map designer, we will build a map for a coach holiday, which we will then integrate into a Node.js-powered website.

Getting Started

Before we can use WRLD’s map designer, we must register. Sign in once you’ve created and verified your account.

Navigate to the Map Designer. This page will list any maps that you have created, as well as allowing you to create new ones. Click on Create Map to launch the designer.

Together, we’re going to create a map for a Dundee food tour. Click New Map on the left and in the resultant New Map modal, enter a title of Dundee Food Tour and then hit Confirm New Map.

Specifying the map's title

Now that we’ve created our map, we need to choose a starting location. Click the menu button next to the search input, click Locations, and choose Dundee.

Selecting a location

Adding Our First Place

To add locations to our map, we have to use WRLD’s Places Designer, which facilitates the creation of place sets that can then be imported and reused.

On the right, click the place marker icon, followed by Manage Place Collections. This will open the designer at the same geographical location. Let’s add our first places collection by clicking New Places Collection. Set the title to Dundee Food Tour and then add a reference to our Dundee Food Tour app, which will link the places collection with our map. Finalise this with Confirm New Collection.

Creating a new place collection

Click New Place Marker next to the search box. A marker should appear on the Dundee Food Tour building, but it can be dragged into place if necessary. In the pane on the right, enter a title of Dundee Food Tour — this will save automatically.

Adding a new place marker

Add as many places as you’d like following this approach, and return to the Map Designer once you feel that you have enough. Our new collection should have been detected and automatically included in our map, but we can configure this manually by clicking the places marker on the right and selecting our place set.

Configuring our places collection for inclusion

Before we display the map on our website, we should set a starting point. Drag and zoom the map until you’ve found an optimal view, then, using the crosshair icon on the right, open the Opening Map View form and click Use Current Map View.

Setting the opening map view

Including the Map on a Website

Despite the impressively-detailed 3D modelling provided by WRLD, the real power of this platform is the ability to embed maps into websites and other applications. As well as exposing a variety of HTTP APIs for directly querying data, there are libraries for rendering maps on many platforms, including iOS, Android, and the web. We will use the wrld.js library, which is also available on npm.

Setting Up

The boilerplate website to which we will add our map can be found at GitHub. Please clone this repository and install the required dependencies:

git clone https://github.com/jamesseanwright/wrld-tours.git
cd wrld-tours
nvm install # this will install the version of Node.js found in .nvmrc. Make sure nvm is installed
npm i

To verify that the project works as expected, run npm run watch; this will start the project using nodemon, restarting automatically when any changes are made. Upon opening http://localhost:8001/trips in your browser, you should see a list of trips with a sole item.

Grabbing and Configuring your Developer Token

Our application will consume data from two of WRLD’s HTTP APIs, which are protected by developer tokens. To acquire your token, sign in and head over to the My Account section, and click the Developer Token tab; make sure you copy it.

Grabbing the developer token via the My Account page

In our app’s codebase, open config.json in the server directory, and paste your token as the value of devToken; this property will be used to make the aforementioned API calls covered in the next step.

{
    "devToken": "insert your dev token here"
}

Calling WRLD’s HTTP APIs

Open the project in your editor, and head to the trips.json file in the server directory. This is a hash map of all of the available trips listed on our website, keyed by a hyphen-separated slug. These are accessible via the /trips route, so we can reach the Dundee Food Tour via /trips/dundee-food-tour. You’ll notice that the entry for the Dundee Food Tour has two empty properties, mapSceneId and poiSetId. We will return to this shortly.

Let’s take a look at the client-side code that fetches the map data. Open TripView.js (not TripViews.js) in the client/views directory and study the onNavigatedTo method. This calls two endpoints that are provided to the client by the server:

const [mapScene, poiSet] = await Promise.all([
    callEndpoint(endpoints.mapScene),
    callEndpoint(endpoints.poiSet),
]);

These don’t directly point to the WRLD APIs. Rather, they are exposed by our own server so that our developer token is not leaked to the browser. Let’s take a look at these routes in server/apiRouter.js:

router.get('/mapscenes/:id', (req, res) => {
    res.status(404).send();
});

router.get('/pois/:poiSetId', (req, res) => {
    res.status(404).send();
});

Currently, they behave as if they do not exist. To implement them, we can use the already-imported request module to forward our requests to the respective WRLD APIs and stream this back to the browser:

router.get('/mapscenes/:id', (req, res) => {
    request.get(`https://wrld.mp/v1.1/mapscenes/${req.params.id}?token=${config.devToken}`)
        .pipe(res);
});

router.get('/pois/:poiSetId', (req, res) => {
    request.get(`https://poi.wrld3d.com/v1.1/poisets/${req.params.poiSetId}/pois/?token=${config.devToken}`)
        .pipe(res);
});

Whenever the client-side JavaScript is evaluated for a trip, it will call these endpoints with the relevant IDs, keeping the developer token away from the browser.

Verifying Our Endpoints

To check that this has worked, we can call our own API endpoints directly. To get the respective IDs for our mapscene and place collection, we can call the WRLD APIs directory with our developer token, i.e.:

  • https://wrld.mp/v1.1/mapscenes/?token=[your dev token]
  • https://poi.wrld3d.com/v1.1/poisets/?token=[your dev token]

We can then take the id properties and pass them to our own local endpoints e.g. http://localhost:8001/api/mapscenes/10786 and http://localhost:8001/api/pois/4149. If we have implemented these routes correctly, then we should receive data from the WRLD APIs.

Updating the Trip Definition

Return to the trips.json file. Remember the two empty properties, mapSceneId and poiSetId? Set their respective values to the IDs you took for the mapscene and place collection e.g.:

"mapSceneId": "10597",
"poiSetId": "4160"

Rendering the Map

We’re now ready to plot the API data onto a browser-rendered map. Let’s return to the TripView.js file.

Below the calls to our endpoints (i.e. Promise.all([...), invoke wrld.map with these precise arguments:

const map = wrld.map(document.querySelector('.map-container'), mapScene.api_key, mapScene);

Using wrld.js, which we have imported into this file as wrld, we are rendering our map into a HTMLDivElement with a class of .map-container, passing the API key and the map data returned by our /api/mapscene endpoint. If you open the Dundee Food Tour trip page in your browser (i.e. http://localhost:8001/trips/dundee-food-tour,) you should see our map with the expected opening view.

Adding Our Places Collection

To wrap up, we will add markers for each place in our collection to the map. In the TripView, implement the addPointsToMap method:

static addPointsToMap(map, points) {
    points.forEach(({ lat, lon, title, height_offset }) => {
        const popup = wrld.popup({
            elevation: height_offset,
            closeOnClick: false,
            closeButton: false,
        });

        popup.setLatLng([lat, lon])
            .setContent(title)
            .addTo(map);
    });
}

Here, we are taking the raw point data returned by the /api/pois endpoint and mapping them into map markers, transforming them as required for the wrld.popup method.

Make sure that the first parameter passed to TripView.addPointsToMap at the bottom of onNavigatedTo is named correctly i.e.:

const map = wrld.map(document.querySelector('.map-container'), mapScene.api_key, mapScene);

TripView.addPointsToMap(map, poiSet);

Upon reloading the page in the browser, we’ll observe that the points have been added to our map, accompanied by the name of each place.

Summary

Web-based maps can easily express information in a variety of contexts, of which we have implemented one with a consumer-orientated focus. With WRLD and a few lines of JavaScript, we have augmented our website’s experience, in turn increasing potential engagement. I hope that this has demonstrated the versatility and importance of maps on the web.