Search for content

Vector Map React App

You will learn how a basic Vector Map with React is implemented. We will display a beautiful vector map with minimal configuration effort. Tutorial illustrates the use of the PTV Developer Vector Maps API

Try it! Download from GitHub

Prerequisites

Getting started

Setting up a React App with CRA

We'll use the official create-react-app (CRA) template as a starting point. Open a terminal at the target location of your project and run the command

npx create-react-app ptv-vector-map-react-app

If this does not work, e.g. because your npm version is older than 5.2.0, you can alternatively run

npm i -g create-react-app
create-react-app ptv-vector-map-react-app

This will create the basic structure of a runnable React App with the name ptv-vector-map-react-app. You can switch into the project folder and run the npm start script to run the app. For now, it will show a rotating React icon.

Writing a Vector Map Component

In this tutorial, we'll use React MapGL to render the vector map. Install the npm package with this command:

npm i react-map-gl@5

We'll use version 5 of the package because newer versions use Mapbox GL JS v2, requiring a Mapbox access token.

In the /src folder, create a JavaScript file called VectorMap.js. Our vector map component may look like this:

// VectorMap.js
import { useCallback } from "react";

import "mapbox-gl/dist/mapbox-gl.css";
import ReactMapGL from "react-map-gl";

// use the standard map style provided by PTV
const MAP_STYLE_URL = "https://vectormaps-resources.myptv.com/styles/latest/standard.json";

export const VectorMap = ({ apiKey, ...props }) => {

  const transformRequest = useCallback(
     (url, resourceType) => (
     { url: url, headers: resourceType === "Tile" ? { ApiKey: " " + apiKey } : {} }),
    [apiKey]
  );

  return <ReactMapGL
    height="100%"
    width="100%"
    mapStyle={MAP_STYLE_URL}
    transformRequest={transformRequest}
  />;
};

The VectorMap component wraps the ReactMapGL component and sets the MAP_STYLE_URL provided by PTV (Read more on styling the map in the concept). Also, it appends the API key passed to VectorMap to the tile requests via transformRequest.

Let's see what this looks like! Replace the contents of the App.js file with this to use the component:

// App.js
import { VectorMap } from "./VectorMap";

// Please enter your API key here
const API_KEY = "YOUR_API_KEY";

const wrapperStyle = {
  position: "absolute",
  gridArea: "map",
  height: "100%",
  width: "100%",
  zIndex: 0
};

const App = () => (
  <div style={wrapperStyle} >
    <VectorMap apiKey={API_KEY}/>
  </div>
);

export default App;

Paste your API key in the designated variable. In a productive app, never hard-code or check-in your API key! You should now see the PTV Vector Map.

Make it interactive

Ok, we see a map, but we can neither zoom nor pan! That's because we are not yet handling the viewport.

Let's adapt our component:

// VectorMap.js
export const VectorMap = ({ apiKey, ...props }) => {

  const [viewport, setViewport] = useState(props.viewport);

  // const transformRequest = ...

  return <ReactMapGL
    // other props
    {...viewport}
    onViewportChange={setViewport}
  />;
};

By keeping the viewport in our component's state, we now can scroll and pan in the map.

Set initial viewport

Most of the time, we would not want the map to initialize on the minimal zoom level (the "world map"), but on some specific position, right? This implementation already sets a viewport you pass to the VectorMap as the initial state passed to the map. Let's set an initial viewport in App.js:

// App.js

// Initializes to Karlsruhe
export const initViewport = {
  longitude: 8.4055677,
  latitude: 49.0070036,
  zoom: 10,
  pitch: 0,
  bearing: 0,
};

const App = () => (
  <div style={wrapperStyle}>
    <VectorMap apiKey={API_KEY} viewport={initViewport}/>
  </div>
);

And just like that, we initialize our map to Karlsruhe, where PTV Headquarters are located.

Adding controls

Now you can go and extend the map component to match your needs. For instance, let's add a NavigationControl from Mapbox to the top-right corner of our map:

// VectorMap.js

const navControlStyle = {
  right: 10,
  top: 10
};

export const VectorMap = ({ apiKey, ...props }) => {
  return <ReactMapGL
    // map props
  >
    <NavigationControl style={navControlStyle} />
  </ReactMapGL>;
};

Now it's your turn! Extend and integrate the PTV Vector Map in your React projects.