Simple Map-Server with MapServer, Shape-Files and OpenLayers

Card services such as Open Street Map or Google Map are really a fine and useful thing. You can easy enter some coordinates and display them, display a route and so on.

But what to do when there is no net available, such as on the high seas or somewhere in the middle of the wilderness? Either you spend a lot of money and get a satellite connection, or you build your own little map server with only a few handles.

The things that you need to do this are all freely available – that is Apache as a web server, MapServer to generate the graphics and OpenLayers to install the graphics in a Web site. Furthermore, you need shapefiles from the area where you want to put the map service.

These can also be found free on the Internet. Or you get the appropriate Shape Files from Open Street Map, provided by Geofabrik .
These shape files are, however, very finely resolved and then you need a little computing power to render the desired images.

I describe here, the installation and configuration on a Raspberry Pi as a mobile map-server.

1. Install Software
So to set up the map server on the Raspberry, you first have to install the necessary software:

 apt-get install apache2 cgi-mapserver gdal-bin

2 Get the Shape files
Now get the desired shape files. I just needed the coastlines of Europe and so I have found this shape:

Europe

This shape has also a not too high resolution and therefore runs on the Raspberry fairly smooth. Even though the file is called world.zip, it is really only Europe 🙂

Then create a folder in the user’s home directory for the Shape files and unzip them into the folder.

mkdir/home/pi/shapes
mkdir/home/pi/shapes/eu
mv world.zip /home/pi/shapes/eu
cd /home/pi/shapes/eu
unzip world.zip

After that the following files should appear in this directory:

pi@raspberrypi ~/shapes/eu $ ls -l
insgesamt 456
-rw-rw-r-- 1 pi pi   9062 Jul 14 19:54 MyEurope.dbf
-rw-rw-r-- 1 pi pi    145 Jul 14 19:54 MyEurope.prj
-rw-rw-r-- 1 pi pi    452 Jul 14 19:54 MyEurope.sbn
-rw-rw-r-- 1 pi pi    148 Jul 14 19:54 MyEurope.sbx
-rw-rw-r-- 1 pi pi 299032 Jul 14 19:54 MyEurope.shp
-rw-rw-r-- 1 pi pi   1795 Jul 14 19:54 MyEurope.shp.xml
-rw-rw-r-- 1 pi pi    364 Jul 14 19:54 MyEurope.shx
-rw-r--r-- 1 pi pi 131025 Jul 14 19:53 world.zip
pi@raspberrypi ~/shapes/eu $ 

3. Configure MapServer
The MapServer itself does not need to be configured, but only the maps and layers, which he is supposed to create. This is done by the setup of so-called Map-Files.
To be able to setup these files properly, you first need some information about the shape.
For this informations the command ogrinfo is used. This is part of the gdal-bin package, which was already installed.

pi@raspberrypi ~/shapes/eu $ ogrinfo MyEurope.shp
INFO: Open of `MyEurope.shp'
      using driver `ESRI Shapefile' successful.
1: MyEurope (Polygon) 

This provides first information on which layers are included in this shape. In this case, it is just one layer with the name MyEurope. Now you need more information about this layer.
These can be get with

pi@raspberrypi ~/shapes/eu $ ogrinfo MyEurope.shp MyEurope -summary
INFO: Open of `MyEurope.shp'
      using driver `ESRI Shapefile' successful.

Layer name: MyEurope
Geometry: Polygon
Feature Count: 33
Extent: (-31.289030, 27.637497) - (30.128712, 69.060303)
Layer SRS WKT:
GEOGCS["GCS_WGS_1984",
    DATUM["WGS_1984",
        SPHEROID["WGS_84",6378137.0,298.257223563]],
    PRIMEM["Greenwich",0.0],
    UNIT["Degree",0.0174532925199433]]
FIPS_CNTRY: String (2.0)
GMI_CNTRY: String (3.0)
CNTRY_NAME: String (40.0)
SOVEREIGN: String (40.0)
POP_CNTRY: Integer (10.0)
SQKM_CNTRY: Real (12.3)
SQMI_CNTRY: Real (12.3)
CURR_TYPE: String (16.0)
CURR_CODE: String (4.0)
LANDLOCKED: String (1.0)
COLOR_MAP: String (1.0)
Pop2011CIA: Integer (9.0)
Pop2000Wik: Integer (9.0)
url: String (100.0)
pi@raspberrypi ~/shapes/eu $ 

The three pieces of information that are needed necessarily for the mapfile are Geometry, Extent and Layer SRS WKT. Here stands for the date „WGS_84“.
Now we can create the first simple mapfile.
For this, we create a directory and create the file eu.map

mkdir /home/pi/maps
nano /home/pi/maps/eu.map

In this file simply enter the following code.

MAP
  # Name of the Map. 
  NAME "Europa"

  # Path to the Shape-Files
  SHAPEPATH "/home/pi/shapes/eu/"
  
  # Size of the Image 
  SIZE 800 600

  # The Extend-Values of the Shape-File
  EXTENT -31.289030 27.637497 30.128712 69.060303
  
  # Type of the Image. You could take more than one .. 
  IMAGETYPE PNG

        # The Projektion of the Shape. WGS_84 = epsg:4326 
        # The EPSG-Values of a Projections can be found in the Net
	PROJECTION
	  "init=epsg:4326"
	END
		 
	WEB
	    IMAGEPATH "/var/www/tmp/"
	    IMAGEURL  "/tmp/"
	    METADATA
	    	"wms_enable_request" "*"
	    END
	END

  # Definition of a Layer
  LAYER
    NAME europe
    DATA MyEurope
    STATUS ON
    # Type of the Shape - output was POLYGON
    TYPE POLYGON
    
    PROJECTION
      "init=epsg:4326"
    END
    
    METADATA
      "wms_title"           "Coastlines Europe"
    END
    CLASS
      STYLE
        COLOR 0 0 0
        OUTLINECOLOR 0 0 0
      END
    END
  END
END

These map files are usually much larger and arbitrarily complicated, but for this example this map file is already sufficient. More information about the Map-Fies can be found on the homepage of MapServer.
Now save the file with ctrl+o and exit the editor ctrl+x. That’s it.
Time for a first test:

If you followed the example and depending on where Apache and Mapserver are installed, the address needs to be adjusted – for example with localhost or IP of the Raspberry.
So open the browser and enter the following link in the address bar:
http://ip-of-server/cgi-bin/mapserv?map=/home/pi/maps/eu.map&SERVICE=WMS&REQUEST=Getmap&VERSION=1.1.1&LAYERS=europe&SRS=EPSG:4326&BBOX=-31.289030,27.637497,30.128712,69.060303&FORMAT=PNG&WIDTH=800&HEIGHT=600

And if all goes goes right, the following screen should appear after a short time.

Shapefile-Test
First Test with the Mapserver

Update:
To make the request clear and not having to specify the map file each time, you can select the following option (described here for Debian / Ubuntu):
Renames the file mapserv in the cgi-bin directory to the name of your WMS service, for example

cp /usr/lib/cgi-bin/mapserv /usr/lib/cgi-bin/my-wms

Then you mus add the following lines in the configuration file of your web server:
For Apache the file /etc/apache2/sites-available/default.conf or the corresponding file
#MapServer applications
SetEnvIf Request_URI „/cgi-bin/my-wms“ map=/home/pi/maps/eu.map

After that the request looks like this:
http://ip-of-server/cgi-bin/my-wms?SERVICE=WMS&REQUEST=Getmap&VERSION=1.1.1&LAYERS=europe&SRS=EPSG:4326&BBOX=-31.289030,27.637497,30.128712,69.060303&FORMAT=PNG&WIDTH=800&HEIGHT=600

Now we have a base map. But this is still completely static. To make the card now interactive and fill it with a bit of life, we need OpenLayers. OpenLayers is a JavaScript library and actually corresponds to what is the Google Maps Api for Google.

So lets build a simple HTML file together with Open Layers:

 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> 
<html> 
    <head>
    
        <style type="text/css">
            div.olControlMousePosition { color: red; }
        </style>
 
        <script type="text/javascript" src="http://openlayers.org/api/OpenLayers.js"></script> 
        <script type="text/javascript">
            function init(){
    
                var center = new OpenLayers.LonLat(10, 49.5);
    
                var map = new OpenLayers.Map( 'map' );

                var baseLayer = new OpenLayers.Layer.MapServer( "Europe Map", "http://ip-of-servers/cgi-bin/mapserv", {map: '/home/pi/maps/eu-test.map', layers: 'europe', srs: 'EPSG:4326', visibility: true } );

                map.addLayer(baseLayer);
                
                var markers = new OpenLayers.Layer.Markers( "Markers" );
                map.addLayer(markers);
                 
                 var myMarker = new OpenLayers.Marker(new OpenLayers.LonLat(13, 52.5));
                 //here add mouseover event
                myMarker.events.register('mouseover', myMarker, function(evt) {
                    popup = new OpenLayers.Popup.FramedCloud("Popup",
                        new OpenLayers.LonLat(13, 52.5),
                        null,
                        '<div>Hallo Welt. Gru&szlig; aus Berlin</div>',
                        null,
                        false);
                    map.addPopup(popup);
                });
                //here add mouseout event
                myMarker.events.register('mouseout', myMarker, function(evt) {popup.hide();});
                markers.addMarker(myMarker);


                map.addControl( new OpenLayers.Control.Zoom() );
                map.addControl(new OpenLayers.Control.MousePosition());
                map.setCenter( center, 4 );
            }
        </script> 
 
    </head> 
     
    <body onload="init()"> 
        <p><h1>Erstes Beispiel</h1><p>
        <div id="map" style="width:800px;height:600px"></div>
        
    </body> 
</html>

If you open this HTML-file now in the browser, you should see something like this:
Our map with a zoom control top left, a marker with a popup and a display of the mouse position at the bottom right.

HTML-Beispiel mit OpenLayers
Erstes HTML-Beispiel mit OpenLayers

Now you can play around and add more marker, Layers, or what whatever you want to the map.

You can see my final product here ( Race Tracker ). As additional WMS-layer there is a map from OpenWeatherMap with the current wind-speed. This is a race tracker from the „Round Britain and Ireland“ race.

Due to construction the server with the race-tracker is not available at the moment.

More information on OpenLayers can be found on openlayers.org

3 Gedanken zu “Simple Map-Server with MapServer, Shape-Files and OpenLayers

    • Hey

      Sorry, unfortunately i’m not an expert in manipulating shape files, but in the map file the necessary part should be the layer definition. It should be something like

      LAYER
      STATUS ON
      # Type of the Shape – output should then be POINT or something like that
      TYPE POINT

      Hope this helps

      Bjoern

Schreibe einen Kommentar