Leaflet (https://leafletjs.com/) is an open source javascript library for serving mobile friendly maps. Look around https://leafletjs.com/index.html for some background on the project and features of the library, as well as documentation.
Check out this repo to your computer and perform the following work in a branch named assignment
:
- Basic knowledge of HTML
- Basic knowledge of Javascript
The following will have been created in a previous assignment but are provided with this codespace to work with:
- PostGIS Database from a previous assignment populated with OSM data from a US state
- Geoserver workspace
osm
- Geoserver layers based on the
osm-styles
repo
Leaflet (https://leafletjs.com/) is an open source javascript library for serving mobile friendly maps. Look around https://leafletjs.com/index.html for some background on the project and features of the library, as well as documentation.
Create a branch named leaflet
and open a Codespace on the leaflet
branch.
The Codespace should initialize to the final state of the Geoserver-OSM-II assignment. That is, with a working PostGIS database populated with OSM data and a working geoserver with nice OSM styles and an osm:osm
layergroup. Details are given in Environment Setup:
This lab is meant to follow up after Geoserver-OSM-Styles. To this end we need a geoserver, a postgis, and the OSM database that you created previously. There is a script in this repo that should initialize all this for you. Run it:
./initialize.sh
Notably, this script does the following:
- Clone the osm-styles repo.
- Download the osm-lowres gpkg.
- Start up
postgis
andgeoserver
withdocker compose up -d
- Wait for
postgis
to be ready and then: - Create the
hawaii
database andpostgis
extension. - Download the
hawaii-latest.osm.pbf
OSM import file from https://geofrabrik.de. - Run
imposm import
to populate the postgishawaii
database - Wait for geoserver to be ready (just in case it's not yet)
- Use the geoserver REST API to fix the osm datastore for the
osm-styles
-based geoserver.
If you open the docker-compose.yml
file you can find the postgis
and geoserver
services. The postgis
service uses a named docker volume while the geoserver
service uses the git clone
d osm-styles repo as its DATA_DIR.
Deliverables are listed at the bottom.
Important: Check the initialization To double check this is configured correctly:
- Check that docker is running
- In the terminal window type
docker ps
.- If no containers are listed, type this to run the initialization script:
bash ./populate_database.sh
- Check your geoserver to ensure that it shows the Hawaii OSM layers:
- Click on
Ports
in theTerminal
codespace panel - Select the
Open in Browser
option - In the browser, add
/geoserver
to the URL to get to the geoserver landing page. - From the geoserver landing page, click
Layer Preview
from the left menu - Find the layer named
osm:osm
and clickOpenLayers
If the page loads a new tab with a Hawaii map then you are good. For any other issue, debug the errors that might be listed in the geoserver log.
In addition to postgis
and geoserver
, we need to run a webserver in order to serve our HTML page that contains leaflet.js map rendering code. The webserver we are using in nginx, the most popular webserver on the web.
The configuration is in nginx/. This also contains:
- nginx/conf.d contains custom configuration to enable this to work in a codespace.
- nginx/html contains content that will be served via http requests. Note that you will be editing and adding files in this directory.
Add the following service to your docker-compose.yml file. Be sure to line up the indentation of the leaflet:
service declaration at the same depth as postgis:
and geoserver:
leaflet:
image: "nginx:mainline-alpine"
ports:
- "80:80"
volumes:
- /workspaces/${RepositoryName}/nginx/html:/usr/share/nginx/html
- /workspaces/${RepositoryName}/nginx/conf.d:/etc/nginx/conf.d
Once you have updated your docker compose file, run
docker compose up -d
Refer to the following https://leafletjs.com/examples/quick-start/
I have included a starter file in this repository to help you get started that represents the working map up to the quick-start section labeled "Markers, circles and polygons"
Open the html/index.html
file in your local browser. To do this, click on the Ports
tab in the VS Code Terminal panel and find Port 80
, which is where the leaflet docker container is running. Open the Local Address
url in a browser. It will have two links: getting-started.html
and geoserver.html
. Click on the getting-started.html
to open a new page containing a leaflet map.
Confirm the map is visible and interactive. It should be zoomed initially to the big island of Hawaii.
Note that this container is actually just running a small webserver serving files from your html
directory in this codespace. There is a single file, index.html
, which is what the web server, nginx, will serve by default if a file path is not given. There is a getting-started.html
file as well. If you add additional files to the html
directory they will be accessible from your web browser by appending the filenames to the Local Address url.
In VS Code, open the html/getting-started.html
file.
There are three sections worth describing.
In the <head>
section, there are two lines that import leaflet-related files:
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" .../>
- Imports the CSS (Styles) for leaflet
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" ...
- Imports the
leaflet.js
javascript library for use in<script>
s
- Imports the
In the <body>
there are two elements to note:
<div id="map" style="width: 600px; height: 400px;"></div>
- This is an HTML element that will be where our slippy map is rendered.
<script>....
- This contains the relevant javascript for initializing our leaflet map.
The main section of this file we are changing is in the <script>
section:
var map = new L.Map('map', { center: new L.LatLng( 19.5429, -155.6659), zoom: 8, attributionControl:true, zoomControl:false});
var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
var osmAttrib='Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors';
var osm = new L.TileLayer(osmUrl, {minZoom: 3, maxZoom: 18, attribution: osmAttrib});
map.addLayer(osm);
To be able to tweak this to your needs, we need to understand how this works. Even if you don't know javascript this should be familiar enough to make modifications to. Let's look at each line individually:
var map = new L.Map('map', { center: new L.LatLng( 19.5429, -155.6659), zoom: 8, attributionControl:true, zoomControl:false});
The line above instantiates a new L.Map
object. This is an object in javascript memory that contains information about a map. As part of the initialization it identifies the map
ID, which corresponds to the <div id="map"... />
HTML element where the map will be rendered
var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
This is just a string that contains a templated line for a tiled map service. The template has placeholders for s
(subdomain), z
(zoom level), x
(x-tile index), and y
(y-tile index). While x
, y
, and z
are easy enough to guess, the s
is an optimization for speeding up the webpage. A map tile provider can serve web tiles from multiple sites in order to accelerate the rate at which a client (i.e., your browser) can request and receive tiles.
var osmAttrib='Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors';
This is another string that just prints attribution data.
var osm = new L.TileLayer(osmUrl, {minZoom: 3, maxZoom: 18, attribution: osmAttrib});
This is another important leaflet.js
function that instantiates a type of Layer
- specifically a TileLayer
based on the osmUrl
and other attributes. At this point it has not been added to the map
object and cannot and has not been rendered.
map.addLayer(osm);
This is the final piece of the puzzle. This adds the osm
TileLayer
to the map
object.
Your first task is to change the getting-started.html
and learn about the interaction of your edits to HTML, javascript, and how it relates to the webpage.
First, change the initial lat/long to 22.05, -159.55
and zoom level to 10
by changing the line that declares var map
:
var map = new L.Map('map', { center: new L.LatLng( 22.05, -159.55), zoom: 10, attributionControl:true, zoomControl:false});
When you reload getting-started.html
you should now see the island of Kauai rendered by the terrain tile map service.
Take a screenshot, making sure the url is visible in the browser screenshot.
Save this as
screencap-osm-kauai.png
Tile maps are a popular way of serving semi-static map data. However, the storage cost of holding many high-res tiles of the full globe is difficult. When this lesson was first taught a set of maps from Stamen (now Stadia) was available. Unfortunately, those are not available. Use your expert googling skills to find a map tile service and replace the OSM service above. As of 11/25/2023, this site lists some free map tiling sites to try: https://github.com/roblabs/xyz-raster-sources.
Take ascreenshot of your new map and save it as:
screencap-tile-map.png
Next we would like to connect a WMS service containing OSM data you downloaded previously and render it on a leaflet map.
Read the example at https://leafletjs.com/examples/wms/wms.html.
Create a brand new file in the html
directory named geoserver.html
and copy the contents of getting-started.html
as a starting point. You are going to add a new layer based on your own geoserver layer group named osm:osm
. Note that you will want to update the initial map coordinates, depending on what state you have chosen for your database. Otherwise anybody using your map will have to pan the world to find your OSM WMS data.
As you read the example, note some differences in the urls of their example. For the geoserver.html
file we will remove the references to var osmUrl
, var osmAttrib
, and var osm
and remove the line that adds the osm
layer to the map. Specifically, we will remove these lines:
var osmUrl='http://tile.stadiamaps.com/toner/{z}/{x}/{y}.png'
var osmAttrib='Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors';
var osm = new L.TileLayer(osmUrl, {minZoom: 3, maxZoom: 18, attribution: osmAttrib});
map.addLayer(osm);
and replace them with this:
var wmsLayer= L.tileLayer.wms("YOUR_CODESPACE_GEOSERVER_URL/geoserver/osm/wms", {
layers: 'osm:osm',
format: 'image/png',
transparent: true
});
map.addLayer(wmsLayer)
Save the geoserver.html
page (make sure it is in the nginx/html
folder) and open it in your browser. There is a pre-made link for you on the index page of the Local Address
landing page. You should see the symbology from your WMS after a brief wait. If not, then something went wrong. In that case, open your Developer Tools and look at the console
and the network
activity to see if you can debug any errors.
Take a screenshot of your working geoserver.html page and save it as:
screencap-leaflet-geoserver-osm.png
Congratulations, you are running a full GIS stack with geospatial backend served by OGC-compliant web services which are consumed by not just your desktop client (QGIS) but also a web client. And to boot, it is all served from a small config file. Even better, you can take this docker-compose.yml
file and run it anywhere that docker can run. It would be trivial to run this in an environment that facilitated auto-scaling so that you could ramp up from one to hundreds or thousands of copies of your webapp to accomodate a dynamic load.
- The Developer Toolbox is your friend!
- screenshot:
screencap-osm-kauai.png
- screenshot:
screencap-tile-map.png
- screenshot:
screencap-leaflet-geoserver-osm.png
- changes to file:
docker-compose.yml
- new file:
nginx/html/geoserver.html