All the attention geographical information systems (GIS) have gotten in recent years may make you think they are something new. In fact, geospatial data helped play a major role more than 160 years ago in identifying the source of the deadly London cholera outbreak of 1854. Dr. John Snow, a local physician, suspected that contaminated drinking water was the source of the disease. During the investigation, he plotted a density map of cholera cases and interviewed residents in the affected neighborhood to learned about their water use habits. His analysis showed a high number of incidents near a communal water pump.
In this article, I'll introduce five modern open source mapping tools, and then help you get started building your first GIS app.
5 tools to know before building your first GIS app
Before diving into the tutorial for building your first GIS app—a map of castles in Prague, Czech Republic—let's look at five essential tools that can make your mapping life easier.
1. Create React App
If you are new to JavaScript and the React ecosystem, creating a "Hello World" app in React from scratch is not a task for the faint of heart. Several libraries and configurations need to be assembled before you can code your first line of JavaScript.
Fortunately, Create React App comes to the rescue. By taking a "batteries-included" approach, the command-line tool developed by the Facebook React team helps you quickly create a fully functional React app by including all the necessary libraries, starter files, and even a unit test framework.
2. GeoJSON.io
GeoJSON is a human-readable format for describing and sharing GIS data that is widely supported by many geospatial libraries and applications. The GeoJSON.io site is an online sandbox for your GeoJSON data.
For example, you can describe the location of the Leaning Tower of Pisa as follows:
{
"type": "Feature",
"properties": {
"description": "Leaning Tower of Pisa"
},
"geometry": {
"type": "Point",
"coordinates": [10.39659, 43.72305]
}
}
To see how your GeoJSON data would look on a map, visit GeoJSON.io and enter the above code.
3. Leaflet
If I could give credit to one open source library for democratizing GIS and bringing geographical maps to web apps, the award would go to Leaflet.js. Created by Vladimir Agafonkin in 2012, the library has since become the de-facto open source JavaScript library for building maps. The API is easy to use and well-documented, and there is a large community on the web and StackOverflow if you ever become stuck.
4. Turf.js
Turf.js is a powerful geospatial library to include in your toolbox. You can perform common geospatial functions, such as testing whether a point lies inside a polygon, or do a more complex k-means calculation to prepare your data for a heatmap.
For example, the following would calculate the distance between San Francisco and Tokyo Narita airport:
const fromSFO = turf.point([-122.3790, 37.6218]);
const toNRT = turf.point([140.3731, 35.7709]);
const options = {units: 'miles'};
var distance = turf.distance(fromSFO, toNRT, options);
5. OpenStreetMap
OpenStreetMap, founded by Steve Coast in 2004, is the largest collaborative mapping project in the world. Similar to Wikipedia, the platform makes it easy for people like you and me to map the world around us by adding and tagging local roads, highways, buildings, rivers, and so on. These map data, available under an open license, are a goldmine for GIS applications! Our tutorial app will connect directly to OpenStreetMap's REST API servers to get all the castles in Prague. Commercial users can download its "weekly planet file," an export of its entire database, an approximately 40GB data file.
Overpass-Turbo
Overpass-turbo is an interactive site for trying out queries against the OpenStreetMap database. To see all castles within 10km of Prague's city center, enter the following query, then click Run. Note that the map does not automatically zoom to our search results. You must enter Prague in the search box to center the map.
[out:json];
node[name="Praha"];
(
way[historic=castle](around:10000);
relation[historic=castle](around:10000);
);
out body;
>;
out skel qt;
Build your first GIS app
To learn how to build a GIS app, let's create a castles explorer app.
1. Create an app with Create React App
Follow the instructions in the Create React App repository to create a working React app.
# mkdir react-map
# cd react-map
# create-react-app .
This will generate a new project structure. Note the following two files, as you will need to edit them in the next step.
- public/index.html: The application's main index.html
- src/App.js: The entry point for React components
Start the app with the following command:
# npm start
2. Set up the Leaflet library and React wrapper
Next, introduce Leaflet and React-Leaflet to the project. The latter is a wrapper library that enables Leaflet to work as a React component.
Install Leaflet and React-Leaflet with the following:
#npm install --save react-leaflet leaflet
Locate public/index.html
in your project and add a link to leaflet.css
as well as some positioning CSS for the main Leaflet div
. The map will be rendered inside this div
container.
<head>
...
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ=="/>
<style>
.leaflet-container {
height: 650px;
width: 800px;
}
</style>
...
</head>
3. Create the first map component
The way to make maps in Leaflet is conceptually similar to how layers are used in GIMP or Photoshop. To display custom data, such as point-of-interest markers, add them to a Leaflet layer, then add that layer to the Leaflet map.
render() {
return (
<Map >
<TileLayer url="tile server url"/>
<YourDataLayerComponent/>
</Map>
);
}
Create a new map component and center the map on Prague, Czech Republic. Place a marker at the city center by editing MyMap.js as follows:
src/MyMap.js
import React from "react";
import { Map, TileLayer, Marker } from "react-leaflet";
export default class MyMap extends React.Component {
constructor(props) {
super(props);
this.state = {
lat: 50.0893,
lng: 14.4284,
zoom: 12
};
}
render() {
const position = [this.state.lat, this.state.lng];
return (
<Map center={position} zoom={this.state.zoom}>
<TileLayer
attribution="&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors"
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[this.state.lat, this.state.lng]} />
</Map>
);
}
}
Now you can wire the MyMap component to the project's entry point component by simply replacing App.js content with the following:
src/App.js
import React, { Component } from "react";
import MyMap from "./MyMap";
class App extends Component {
render() {
return <MyMap />;
}
}
export default App;
Your browser should now show the map of Prague! You can zoom and pan the map with your mouse.
4. Display castles on the map
In this step, you will extract building structures tagged as historic=castle
in OpenStreetMap and display them on your map. Instead of rolling your own Rest API client, you can enlist the query-overpass
, a library that fetches data from OpenStreetMap's API server, with the following command:
#npm i --save query-overpass
Create a new file for your castles component in Castles.js:
src/Castles.js
import React from "react";
import { GeoJSON, Marker } from "react-leaflet";
import * as overpass from "query-overpass";
export default class Castles extends React.Component {
constructor(props) {
super(props);
this.state = {
geojson: undefined
};
}
componentDidMount() {
const query = `[out:json];(way[historic=castle](around:10000, 50.0874654,14.4212535);\
relation[historic=castle](around:10000, 50.0874654,14.4212535););\
out body;>;out skel qt;`;
const options = {
flatProperties: true
};
overpass(query, this.dataHandler, options);
}
dataHandler = (error, osmData) => {
if (!error && osmData.features !== undefined) {
this.setState({ geojson: osmData });
}
};
render() {
return this.state.geojson ? <GeoJSON data={this.state.geojson} /> : null;
}
}
Add the castles component to your map in MyMap.js:
src/MyMap.js
import Castles from "./Castles";
...
render() {
const position = [this.state.lat, this.state.lng];
return (
<Map center={position} zoom={this.state.zoom}>
<TileLayer
attribution="&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors"
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[this.state.lat, this.state.lng]} />
<Castles />
</Map>
);
}
What's next?
If you examine the JSON returned from Overpass, you will see a host of information, such as each castle's physical address, name in Czech, Wikipedia article, and more. You can add a popup to show other details when users click on a castle.
Also, the query is hard-coded to search for castles within a 10km radius of Prague's city center. You can improve the castles component and the query to handle arbitrary latitudes and longitudes, effectively turning the app into a global castle explorer!
3 Comments