Skip to main content

In this article, we will learn how to create a web application to search for the world’s seven wonders. The user will be shown a list of the seven wonders, and the map will transition to the appropriate location based on the user’s choice. To render the map and handle all its related logic, we will use NextBillion.ai, but more on this later.

We’ll be using React along with the NextBillion.ai NPM package. In the end, our application should be very similar to this:

💡  Here is the GIF.

Excited already? Let’s start building!

Prerequisites

The only prerequisite to making this app is basic familiarity with React. You need to know about: 

  • Component-based architecture in React.js
  • useState and useEffect hooks

If you want to brush up on these points, official React documentation is a good place to start.

You don’t need to be a master of maps either, for this project. NextBillion.ai has ready-to-use functions and hooks that can be imported directly from the library. However, there are a few terms from the mapping ecosystem that we will discuss here to avoid later confusion:

  • Coordinates – Coordinates are the precise latitude and longitude values of a particular location. Every place on the Earth has a latitude and a longitude that uniquely identifies its geographical position. In our app, we will use decimal degrees to work with coordinates.

Marker – A marker identifies a location on the map. In most scenarios, it will point toward the coordinates of the selected place. With NextBillion.ai, you can set a custom icon as a marker, but for the sake of simplicity, we will stick to the default one.

source of images: internet

Let’s Build

Enough of the theory; let’s start with building the application. 

Access Credentials and Dependencies

To use any NextBiliion.ai product or service, you’ll need its API key. You can fill out a form to get yourself a new API key. The form asks for minimal information — the API service you need and the expected volume of API calls per month.

Once you get the API key, we recommend you put it in the .env file rather than hardcoding it. Also, don’t forget to add the .env file in the .gitignore (or your version control equivalent) file, so that you won’t commit it in the remote repo.

The second thing you’ll have to do is install the NPM package released by NextBillion.ai. To install it with NPM, just enter the following command:

 

npm install nbmap-gl

Or if you use Yarn:

yarn add nbmap-gl

Everything related to NextBillion.ai will be installed in the node_modules folder. You can find all JavaScript, CSS, and Map files in node_modules/nbmap-gl/dist.

Code Architecture

Let’s briefly discuss how we’re going to build this app. The map is the most important part of the project, so we’ll make a separate component that will handle its business and presentation logic. We will also create a dropdown component to handle dropdown and data change.

We’ll need to store the selected wonder and its coordinates, so we will create two states in App.js. Everything else can be declared as we go.

For now, let’s create two components, Map.jsx and Dropdown.jsx, and initialize them with mock data. Inside App.js, create two states coordinates and wonder. The former will hold the coordinates of the selected wonder while the latter holds its name. To illustrate this, I’m initializing coordinates with the geolocation of the Taj Mahal, but feel free to keep it null or enter your desired destination. Make sure it’s a valid set of geocoordinates to avoid errors.

 

const [coordinates, setCoordinates] = useState({
	lat: 27.1773531,
	lng: 78.0116069
});

const [wonder, setWonder] = useState("taj-mahal");

Creating a Map

Now we’re done with the skeleton, let’s focus on the main task. We have already installed the necessary packages and created a map component. This component will display a map centered on the selected wonder.

First things first — import dependencies and set the API key. In your Map.jsx, paste the following code:

 

import nextbillion from "nbmap-gl";
import "nbmap-gl/dist/nextbillion.css";

nextbillion.setApiKey(process.env.REACT_APP_NB_API_KEY);

This is a good checkpoint to test whether the API is working and that all dependencies and packages are installed properly. If you encounter any issues at this stage, check the status of your API.

The Map.jsx will receive coordinates and setCoordinates as props and return only a single div with an id of map. Everything else will be taken care of by inbuilt functions provided by the NextBillion.ai package.

function Map({ coordinates, setCoordinates }) {
	return <div id="map"></div>;
}

export default Map;

Don’t forget to give it a fixed height. Otherwise, it won’t be rendered properly.

 

#map {
	width: 100vw;
	height: 80vh;
}

Create two state variables, map and marker, which will store the map’s current state and the marker’s position.

 

const [map, setMap] = useState(null);
const [marker, setMarker] = useState(null);

Initializing the Map

We want the map to be displayed on screen as soon as the component renders. The useEffect hook with an empty dependency array is how we make it happen.

useEffect(() => {
	const nbMap = new nextbillion.maps.Map(document.getElementById("map"), {
	zoom: 12,
	center: coordinates
	});
	
	setMap(nbMap);
}, []);

Here, we create a new instance of Map that takes two parameters: the DOM node where we want to render our application (in this case, #map) and an object with details about the map. The most widely used options are zoom and center. zoom defines how zoomed in we want our map to be in the beginning, whereas center is the coordinate value where the map should focus.

This creates an initial map state that’s stored in the state variable.

We can continue with this or make it more visually pleasing. NextBillion.ai allows customization of the map’s styling and vector tiles. We can link any valid JSON source, but for now, we will use NextBillion.ai’s stylings.

Add two more properties to the object, vectorTilesSourceUrl and style as follows:

useEffect(() => {
  const nbMap = new nextbillion.maps.Map(document.getElementById("map"), {
    zoom: 12,
    center: coordinates,
    vectorTilesSourceUrl: "https://api.nextbillion.io/tiles/v3/tiles.json",
    style: `https://api.nextbillion.io/maps/streets/style.json?key=${process.env.REACT_APP_NB_API_KEY}`
  });

  setMap(nbMap);
}, []);

Let’s check the map on the browser now.

It looks much better! 

Note that we can link any external JSON file as long as it follows the standard of vector tile JSON.

Positioning the Marker for the First Time

We want the marker to be positioned at the center that we defined in the previous stage, and we want it to happen when the map is rendered on the screen. Again, we will use useEffect, but this time, keep map in the dependency array.

 

useEffect(() => {	
	try {
		const nbMarker = new nextbillion.maps.Marker({		
			position: coordinates,		
			map: map	
		});
	setMarker(nbMarker);	
	} catch (error) {
	console.error(error);	
	}
}, [map]);

Since the map is initialized as null, it will throw an error when rendering the component. This is why we put the logic in the try-catch block — so errors can be handled without much difficulty.

Readjusting the Map When a Wonder is Selected

When a user selects any wonder, the map and marker should readjust to be centered on the geolocation of the wonder. The NextBillion.ai package includes methods that animate the transition of the map and marker.

We call our old friend useEffect here for help. This should happen when coordinates change, but to avoid possible errors, add a map and marker to the dependency list.

 

useEffect(() => {
marker &&
	map && (
		marker.moveTo(coordinates, 9),
		map.flyTo({ center: coordinates, zoom: 12, speed: 9.0, curve: 0.8 })
	);
}, [map, marker, coordinates]);

This looks complicated at first glance, but if you look closer, you’ll see that it’s just the same procedure we used before with a few extra parameters.

The marker.moveTo() method moves the position of the marker to the location mentioned in the first parameter with the speed mentioned in the second parameter. The speed could be any number from 0 to 10 (inclusive), where a lower number denotes a slower speed.

The second method, map.flyTo(), does something similar to the map. It changes to a new center and animates the transition for you. It takes two parameters: center and options. ‘Options’ describes the animation and destination of the transition, and takes three main properties: curve, speed and zoom. The ‘curve’ parameter decides the zooming curve that will occur during the transition. The speed is dependent on the value of the curve, and ‘zoom’ decides the level of zoom when the transition is complete.

The User Input

The map component is completed. You can play around with the coordinates state to test if everything is working as it should. The final step is to build up the logic for users to select the wonder.

An easy way to achieve this is to create a dropdown with the seven wonders as options, and change the coordinates per selection. Let’s start with creating the skeleton of the component.

 

import { useEffect } from "react";

function Dropdown({ wonder, setWonder, setCoordinates }) {
	return <section className="dropdown"> Dropdown </section>;
}

export default Dropdown;

The component doesn’t need to store any state by itself, since everything it needs is sent by the App component as props. Once integrated with the App component, return a dropdown with the seven wonders, like this:

 

return (
	<section className="dropdown">
		<select name="wonders" onChange={(e) => setWonder(e.target.value)}>
		<option value="taj-mahal">Taj Mahal</option>
		<option value="colosseum">Colosseum</option>
		<option value="chichen-itza">Chichen Itza</option>
		<option value="machu-picchu">Machu Picchu</option>
		<option value="christ-the-redeemer">Christ the Redeemer</option>
		<option value="petra">Petra</option>
		<option value="great-wall">Great Wall of China</option>
		</select>
	</section>
);

The dropdown will change the value of wonder whenever a new wonder is selected from the list. The final piece of the puzzle is changing coordinates as the user selects each wonder. We can use a switch statement to toggle between multiple states.

 

useEffect(() => {
	switch (wonder) {
		case "taj-mahal":	
			setCoordinates({ lat: 27.1773531, lng: 78.0116069 });	
			break;	
		case "colosseum":	
			setCoordinates({ lat: 41.8902141, lng: 12.4877462 });	
			break;	
		case "chichen-itza":	
			setCoordinates({ lat: 20.6787867, lng: -88.5706656 });	
			break;	
		case "machu-picchu":	
			setCoordinates({ lat: -13.163136, lng: -72.5471516 });	
			break;	
		case "christ-the-redeemer"	
			setCoordinates({ lat: -22.9533291, lng: -43.2132448 });
			break;	
		case "petra":	
			setCoordinates({ lat: 26.9965639, lng: 33.5115078 });	
			break;	
		case "great-wall":	
			setCoordinates({ lat: 40.4319117, lng: 116.565892 });	
			break;	
		default:	
			setCoordinates({ lat: 28.6139, lng: 77.209 });	
	}
}, [wonder]);

That’s it! Open your browser and test the live demo of the application. If something is not working as it should, check your console and see if you have any errors or warnings. If you are still stuck, look at this Repl and see if you’re missing anything:

Finishing Touch

Before we wind up, let’s style our application a bit. Paste the following CSS in your /src/App.css:

h1 {
  font-size: 2.5rem;
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  text-align: center;
}

.dropdown {
  margin-bottom: 1.5rem;
  display: flex;
  justify-content: center;
}

select {
  padding: 0.5rem;
  border-radius: 15px;
  font-size: 1rem;
  font-family: Verdana, Geneva, Tahoma, sans-serif;
}

select:hover {
  cursor: pointer;
}

#map {
  width: 100vw;
  height: 80vh;
}

Needless to say, if you are working on a bigger application, you need to have more specific identifiers in place of this.

Conclusion

Integrating maps into your website is one of the trickiest parts of front-end development. Though many solutions are available in the market, very few are feasible for diverse scales. Most available products are costly and would likely require you to increase your overall budget to integrate them fully. Others are complicated and require expertise to integrate them into your products. Either way, small- to medium-scale businesses face many difficulties integrating maps with their products. NextBillion.ai aims to solve this issue with a different approach.

🎉 NextBillion.ai Raises Series B Funding

X