How to integrate OpenStreetMap into React Apps

As a developer, you may need to work with google maps. Many articles tell you how to work with Google maps. But, there is one issue, especially, for new developers or those who want to show their projects to potential employees. Google Maps may be accessed free of charge for a specific limit, but you also have to use your debit/ credit card information to use the limited free Google API services. Personally, I found that this is somewhat discouraging and an obstacle for the new developers when they want to showcase their work to potential employers. So, I decided to show you an alternative to Google Maps.

OpenStreetMap, an alternative to Google Maps

I was working on a demo yelp app. While I was developing the app, I realized the above mention issues with Google Maps. I was searching for alternatives to google maps. I was frustrated there are not much to satisfy my needs. Most such Map API has limits. 

Then, finally, I came across OpenStreetMap, an open-source project initiated to create and distribute geographic data for the whole world. As I mentioned before many such services have financial, legal, and technical restrictions. And The purpose of OpenStreetMap is to overcome these barriers and promote the creativity of the developers. 

Unfortunately, I was not able to find much information on using OpenStreetMap, especially with examples on how to use it. Anyway, with a lot of work, I became somewhat familiar using OpenStreetMap and related tools. So, I will share my experience in using OpenStreetMap and develop a small app for you.

Is OpenStreetMap better than Google Maps?

Yes, Google maps are more accurate, has higher quality images, and are easy to integrate into your apps in comparison to OpenStreetMap. But, of course, it has a price tag. What about OpenStreetMap?

In my opinion, it is not a bad tool, especially as a free tool. It does need improvements in the quality of the images and accurate positioning based on the user input. However, I highly recommend for a developer integrate it into your personnel projects. It is also a good solution for small businesses with low-end budgets on web development projects.

Leaflet and React-leaflet

OSM( OpenStreetMap ) use Leaflet . a javascript library for interactive maps. It is lightweight and mobile-friendly. 

React-leaflet is another javascript library that connects React applications with Leaflet. React-leaflet makes use of Leaflet, uses leaflet layers, and presents it as React components.

When you work with OSM, you must be familiar with both Leaflet and React-leaflet. However, a basic understanding is enough to get started with a demo app. 

How to integrate OSM into your React project

We are going to develop a small app. The functionality is limited. But, it does what we need: Learning how to use OSM for your projects.

While you can do this on your local computer, I would encourage you to use an online IDE such as codesandbox.io or replt.it that supports React projects. The main reason is you may come across an issue with compatibility when you use different versions of libraries. You may not know which library support which one. However, if you use the default setting of the online IDE, you can avoid these compatibility issues. Then if you want, you can download this project to your local host and do the development.

Note: if you decided to download and develop, you might have to delete some dev dependencies. It is hard to tell for each case. For example, I used codesanbox.io. While I was developing my project, I also downloaded it to my local host. I tried to run the project. But, I could not install the node modules with “npm install”. I was checking the error. Then, I found there were dev dependencies for Typescript support in package.json. 

As I do not need Typescript in my project, I simply delete that dev dependency and run “npm install”. Everything went fine 🙂

What we are going to do is a precise address mapping web application. The user would enter an address as accurately as possible and the app will show the location on a map. 

The app should work anywhere in the world since OSM supports geographic data worldwide.

Demo App

File structure

You can use tools such as create-react-app if you are using your localhost to set up the React files structure. I used codesandbox.io.

App.js

App.js has the main logic of this application. It uses 3 state objects to hold coordinates, display name, and address. My personal recommendation for you is to keep the coordinates in one state and other data on location in another state or states.

import "./styles.css";
import Map from "./Map/Map";
import { useEffect, useState } from "react";
export default function App() {

  const [coords, setCorrds] = useState({
    latitude: "",
    longitude: ""
  });
  const [display_name, setName] = useState("");
  const [address, setAddress] = useState({});

  useEffect(() => {
    navigator.geolocation.getCurrentPosition(
      getCurrentCityName,
      error,
      options
    );
  }, []);

  function error(err) {
    if (
      err.code === 1 || //if user denied accessing the location
      err.code === 2 || //for any internal errors
      err.code === 3 //error due to timeout
    ) {     
      alert(err.message);
    } else {
      alert(err);
    }
  }

  const options = {
    enableHighAccuracy: true,
    maximumAge: 30000,
    timeout: 27000
  };

  //get current location when the app loads for the first time
  function getCurrentCityName(position) {
    setCorrds({
      latitude: position.coords.latitude,
      longitude: position.coords.longitude
    });

    let url = "https://nominatim.openstreetmap.org/reverse?format=jsonv2" +
      "&lat=" + coords.latitude + "&lon=" + coords.longitude;
      
    fetch(url, {
      method: "GET",
      mode: "cors",
      headers: {
        "Access-Control-Allow-Origin": "https://o2cj2q.csb.app"
      }
    })
      .then((response) => response.json())
      .then((data) => setName(data.display_name));
  }

  //get input from text fields and append it to address object
  function update(field) {
    return (e) => {
      const value = e.currentTarget.value;
      setAddress((address) => ({ ...address, [field]: value }));
    };
  }

  //send the data on the state to the API
  function getData(url) {
    fetch(url, {
      method: "POST",
      mode: "cors",
      headers: {
        "Access-Control-Allow-Origin": "https://o2cj2q.csb.app"
      }
    })
      .then((response) => {
        if (response.ok) {
          return response.json();
        }
      })
      .then((data) => {
        setName(data[0].display_name);
        setCorrds({
          latitude: data[0].lat,
          longitude: data[0].lon
        });
      })
      .catch(() => error("Please Check your input"));
  }

  //set form input( data entered ) to state on form submit
  function submitHandler(e) {
    e.preventDefault();
    console.log(address);

    let url = `https://nominatim.openstreetmap.org/search?
    street=${address.street}
    &city=${address.city}
    &state=${address.state}
    &country=${address.country}
    &postalcode=${address.postalcode}&format=json`;

    getData(url);
  }

  return (
    <div className="App">
      <h1>Enter The address</h1>
      <section className="form-container">
        <form>
          <label>street:</label>
          <input
            value={address.street}
            placeholder="1234 abc street"
            onChange={update("street")}
            id="street"
            type="text"
          />
          <label>city:</label>
          <input
            placeholder="Los Angeles"
            type="text"
            value={address.city}
            onChange={update("city")}
            id="city"
          />
          <br />
          <label>State:</label>
          <input
            placeholder="CA / California"
            type="text"
            value={address.state}
            onChange={update("state")}
            id="state"
          />
          <label>zip code:</label>
          <input
            placeholder="91423"
            type="text"
            value={address.postalcode}
            onChange={update("postalcode")}
            id="postalcode"
          />
          <br />
          <label>Country:</label>
          <input
            placeholder="USA"
            type="text"
            value={address.country}
            onChange={update("country")}
            id="country"
          />
          <br />

          <button onClick={(e) => submitHandler(e)}>Search</button>
        </form>
      </section>
      <Map coords={coords} dispaly_name={display_name} />
    </div>
  );
}

Integrating the Map

  • create a folder named “Map” in the src folder
  • create a file name Map.jsx in the Map folder
  • Enter the following code to Map. jsx
  • React-leaflet uses React Context. The MapContainer component creates an instance of Leaflet Map and the context it uses.
  • <TileLayer> component is required to display tile layers on the map. It has an attribution prop and url prop. The attribution prop is optional and you can change it according to your needs. The url prop is required to render the map.
  • <Marker> component creates a clickable and draggable icon on the map. You can create your own icon. The Marker component has two props in this demo app. position ( position on the map) and icon(default or custom icon). The Position prop is required, and if you do not specify the icon prop( a custom icon) , a default icon is used. ( I created a custom icon. I used the resources at https://unpkg.com/browse/[email protected]/dist/images/ for the image)
  • <Popup> displays any important information about the location for the user when he /she click on the Marker. It is not required but can be very helpful for the end-users.

Map.jsx

import { MapContainer, TileLayer, Marker, Popup, useMap } from "react-leaflet";
import React from "react";
import "leaflet/dist/leaflet.css";
import icon from "../Images/icon.png";
import L from "leaflet";

export default function Map({ coords, display_name }) {
  const { latitude, longitude } = coords;

  const customIcon = new L.Icon({//creating a custom icon to use in Marker
    iconUrl: icon,
    iconSize: [25, 35],
    iconAnchor: [5, 30]
  });

  function MapView() {
    let map = useMap();
    map.setView([latitude, longitude], map.getZoom());
     //Sets geographical center and zoom for the view of the map
    return null;
  }

  return (
    <MapContainer
      classsName="map"
      center={[latitude, longitude]}
      zoom={10}
      scrollWheelZoom={true}
    >
      <TileLayer
        attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> 
        contributors'
        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      />
      <Marker icon={customIcon} position={[latitude, longitude]}>
        <Popup>{display_name}</Popup>
      </Marker>
      <MapView />
    </MapContainer>
  );
}

A Note on the usage of this demo app

As I was experimenting with this app, I found the following observations.

1. ) If you enter, for example, “New York” as the city and “USA” as the country in a first attempt, the app may not be able to locate the city of New York. However, if you provide a precise location in your first attempt and search for that location, and then if you entered “New York” as the city and “USA” as the country in your second attempt, the app will find the city of New York. SO, I RECOMMEND YOU START YOUR FIRST INPUT WITH A PRECISE LOCATION AND THEN DO YOUR EXPERIMENTS. 🙂

The first attempt: search for precise location, ideally filling all /most of the form fields

second attempt: you may approximate the positioning on partial input. For example;

City: New York country:USA //leave other input fields empty
Country:sri lanka //leave other input fields empty

2. ) If you enter the address of your apartment, you may not find the precise place. But. if you enter an address of a public place, it will find it. In this case, you can remove the “street” field on your input (keep it empty) and fill in other fields. Then, it will find the city you live.

This application is not the most accurate application, and it could be improved. Please make sure this is only and demo app with the purpose of teaching OSM. Having said that my personal opinion is that these inaccuracies could be partly due to errors in OSM. As I mentioned before OSM still needs improvement to compete with Google Maps.

You can find the full code in my git repo for this demo project

Final thoughts…

I highly encouraged you to use OSM in your projects. OSM is also used in commercial applications. It has similarities with google maps when it comes to coding the Map component. As a new developer, if you get familiar with OSM, you can definitely apply that know-how to set up google maps in future projects. And more than anything else, OSM gives you the freedom to create the next innovative web application.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top