Using MapServer to feed tiles to Google Maps

While working on the CMP‘s China Media Map, I had the chance to use MapServer, an Open Source platform for publishing spatial data and interactive mapping applications on the Web.

We start with a need: display big datasets of geo-tagged data in an efficient manner for the Web. What do you do? We have over 4000 data points corresponding to individual news agencies in China. Almost each of these points (containing an address) were geotagged using Google’s geocoding service (method shown in another entry). All these points are modeled in a Postgis-enabled PostgreSQL database. We use Google Maps as our display platform. How can we display the points?

One of the early versions, which I knew wouldn’t work in a production environment, was to generate all points as markers. Of course, when you are in the lowest zoom level, at the country-level (China, here), 4000 points risks crashing any more or less recent and decent computer. The alternative is to give users a preview of those 4000+ points, without creating the marker object. What do you do? You check this video out first:

This was shown at Google I/O 2009, and the part of interest is at about 26:31 into the video. This is where the presenter talks about tiny markers. How does Google do it? They don’t actually go into details in the video.

MapServer is the tool you need to use. One of its many uses is to help you create your own custom layer. As you might know, Google Maps works by feeding you 256x256px squares of their map data. These tiles are generated by Google’s in-house map tiles server, which gives the following:

http://mt0.google.com/vt/lyrs=m@124&hl=en&x=836&y=446&z=10&s=Ga

But it is possible to produce your own, with data coming from your own spatial database. Google has an outline of how it’s possible to integrate these easily with their version 3 of the Google Maps API (still in Labs).

You may use MapServer, which has binaries available the major platforms (Windows, Mac, Linux). I use Linux, the Ubuntu distro, so all I had to do was set up the UbuntuGIS Personal Package Archive (PPA) in Unstable to get the latest version of all the GIS software you need.

Use a configuration file such as the one I have: news.map

MAP
	DEBUG	5
	CONFIG "MS_ERRORFILE" "/home/csam/geo/mapserver/error.log"
	FONTSET "/home/csam/geo/mapserver/fontset.txt"
	IMAGETYPE	PNG
	#EXTENT	105 15 132 54
	EXTENT	-40.900557 -99.293024 68.194131 174.885971
	#EXTENT	-141.018073 41.676949 -52.582296 89.999427
	SIZE	256 256
	IMAGECOLOR	255 255 255
	TRANSPARENT	ON
	OUTPUTFORMAT
		NAME png
		DRIVER "GD/PNG"
	MIMETYPE "image/png"
		IMAGEMODE RGB
	EXTENSION "png"
	FORMATOPTION "INTERLACE=OFF"
	END
	PROJECTION	"init=epsg:4326"
	END
	WEB
	METADATA
		WMS_SRS "EPSG:4326 EPSG:900913"
	END
	END
	LAYER
		NAME		news_overview
	CONNECTIONTYPE POSTGIS
	CONNECTION "YOUR_OWN_CONNECTION_STRING"
	DATA	"point FROM (SELECT g.gid, g.point, na.name AS placename FROM google_geocoding AS g LEFT JOIN news_agencies AS na ON g.query = lower(na.address) WHERE na.new_id IS NOT NULL UNION SELECT g.gid, g.point, na.name AS placename FROM google_geocoding AS g LEFT JOIN news_agencies AS na ON g.query = lower(na.name) WHERE na.address IS NULL AND na.new_id IS NOT NULL) AS subquery USING UNIQUE gid USING srid=4326 "
	STATUS	ON
	TYPE	POINT
	CLASS
		NAME	"Points"
		STYLE	
		SIZE	3
		COLOR   255 0 0 
		END
	END
	END
	LAYER
		NAME		news
	CONNECTIONTYPE POSTGIS
	CONNECTION "YOUR_OWN_CONNECTION_STRING"
	DATA	"point FROM (SELECT g.gid, g.point, na.name AS placename FROM google_geocoding AS g LEFT JOIN news_agencies AS na ON g.query = lower(na.address) WHERE na.new_id IS NOT NULL UNION SELECT g.gid, g.point, na.name AS placename FROM google_geocoding AS g LEFT JOIN news_agencies AS na ON g.query = lower(na.name) WHERE na.address IS NULL AND na.new_id IS NOT NULL) AS subquery USING UNIQUE gid USING srid=4326 "
	STATUS	ON
	TYPE	ANNOTATION
	CLASS
		NAME	"pimple"
		STYLE
		SYMBOL	"pimple"
		SIZE	10
		COLOR   0 0 0 
		END
			LABEL
				PARTIALS FALSE
				FORCE TRUE
			END
		TEXT ' '
	END
	END
	SYMBOL
		NAME 'pimple'
	TYPE PIXMAP
	IMAGE "google-pimple.png"
	TRANSPARENT 0
	END

	LAYER
		NAME		labels
	CONNECTIONTYPE POSTGIS
	CONNECTION "YOUR_OWN_CONNECTION_STRING"
	DATA	"point FROM (SELECT g.gid, g.point, ST_AsText(g.point) AS labl FROM google_geocoding AS g LEFT JOIN news_agencies AS na ON g.query = lower(na.address) WHERE na.new_id IS NOT NULL ) AS subquery USING UNIQUE gid "
	STATUS	ON
	TYPE	ANNOTATION
	MAXSCALE 100000000000 
	LABELITEM labl
	CLASS 
		LABEL 
		ANGLE auto 
		SIZE 10 
		COLOR 192 0 0
		TYPE truetype 
		FONT arial
		END
	END 
	METADATA
		WMS_SRS "EPSG:4326 EPSG:900913"
	END
	END
END

Point to the mapserv CGI with the appropriate arguments. map is the path to your mapscript configuration file, and mode is tile for this purpose, and tilemode will be gmap for Google Maps (could be Bing Maps and OpenStreetMap too).

This is a link to a tile on the China Media Map:
http://jmsc.no-ip.org/cgi-bin/mapserv?map=/home/csam/geo/mapserver/news.map&layers=news&mode=tile&tilemode=gmap&tile=3+1+2

Both the MapServer and PostGIS documentation sites give you examples of how to write the configuration file. The open source geospatial consortium also has a page for showing you what PostGIS is, and how to use MapServer with it for the Web.

2 responses to “Using MapServer to feed tiles to Google Maps”

  1. rmxz says:

    Very nice maps.

    Curious what font you used.

    I’m trying to get both english and chinese characters to work like in your screenshot; and seem to always having trouble with one of the two.

Leave a Reply