Consolidating real time traffic CCTV data from data.gov.hk with Goolge Map API

Real time CCTV snapshot images from major route and highways in Hong Kong are provided online at data.gov.hk. Considering availability of data and related technology in the last decade, we have made a huge progress from obtaining traffic jam information from the radio to real time traffic CCTV images on the web.

Advancement in web technology not only enabled access of information, but also provided information in a manner flexible enough to easily build new view of content from multiple sources. The term “mashups” generally refer to this type of web application, and one such example presented here being visualizing road traffic data in near real time images on the web. A demo page is available on this site.

As explained in a previous article demonstrating a practical neural network application, CCTV images from the Hong Kong road network are already made available online. Together with Google Map API, an integration of the two will no doubt a very interesting fusion of technology.

Firstly, visit data.gov.hk for a glimpse of the traffic data available. Each CCTV provides image data on the web via a fixed URL.

The next thing to do is to integrate these data in Google Map API. In an attempt for a quick demonstration, a simple out-of-the-box application is adapted to retrieve data from data.gov.hk. These include the camera image, and the best of all, the geographical information from each of these images, i.e. the longitude and latitude. data.gov.hk provided a complete list of geographical data of its cameras, but since they are encoded in a format not directly understood by Google Map (HK80), a translation is required.

After confirming the position of each camera, a simple CSV file is created and the resulting Javascript is generated using an AWK script. There can’t be a method possibly be simpler than this when a prototype page with the Google API has been worked out. The below is captured from the resulting web page showing a selection of location of interest from all available CCTV images from data.gov.hk.

hkdatagovcctv2

The map below in HTML created using Google Map API showing a complete list of CCTV location available at data.gov.hk. Clicking on each marker will open the real time image from the corresponding CCTV camera.hkdatagovcctv1

Excel is used to output a CSV data file for generating the Javascript file for use in the above example. The original file is in PDF but it is fairly easy to convert into Excel and then export from local coordinates in data.gov.hk (HK80) to something Google Map API can understand and render.hkdatagovcctv3

The layout of the CSV file is largely duplicated from the PDF file, and the line below is a sample. Most fields are self-explanatory, only the last three fields are additional to the data from the PDF file. Two of these are the converted coordinates for Google Map, and the last one is the default position of the CCTV image with respect to the marker. This can be either one of C,Q,W,A,S. For Q,W,A,S the image will be displayed on the corresponding quadrant as these characters are laid out on the keyboard, while C represent the display centered below the marker.


H106F,H106F.JPG,Connaught Road Central near Exchange Square,http://tdcctv.data.one.gov.hk/H106F.JPG,816365,834060,22.2861747,114.1554513,C

The AWK script below is used to loop through the CSV file to generate the part of the HTML content for each CCTV data.

BEGIN{
	FS=",";
	idx=1
}

{
	print "y0=" $7 ";x0=" $8;
	print "y1=y0+0.04;x1=x0+0.05;"
	print "var bounds" idx " = new google.maps.LatLngBounds(new google.maps.LatLng(y0,x0),new google.maps.LatLng(y1,x1));"
    print "var srcImage" idx " = '" $4 "';"
	print "var marker" idx " = new google.maps.Marker({position:new google.maps.LatLng(y0,x0),map:map,title: '" $1 "\\n" $3 "'});"
	print "var quadrant" idx " = \"C\";"
    print "overlay" idx " = new TrafficImageOverlay(bounds" idx ", srcImage" idx ", quadrant" idx ", map);"
	print "arr.push(overlay" idx ");"
	print "marker" idx ".addListener('click', function() {overlay" idx ".toggle();});"
	if ($9=="C") {
		print "overlay" idx ".toggle();"
	}
	idx++;
}

Finally, the HTML file.

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
    <meta charset="utf-8">
    <title>Hong Kong Real Time Road Traffic Camera Image</title>
<style>
      /* Always set the map height explicitly to define the size of the div
       * element that contains the map. */
      #map {
        height: 100%;
      }
      /* Optional: Makes the sample page fill the window. */
      html, body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>

    <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_SECRET_GOOGLEMAP_KEY"></script>
    <script>

    var overlay;
	var arr = [];
    TrafficImageOverlay.prototype = new google.maps.OverlayView();
    TrafficImageOverlay.prototype.toggle = function() {
      if (this.getMap()) {
		if (this.quadrant_=="A") {this.quadrant_="Q"; this.setMap(null); this.setMap(this.map_);}
		else if (this.quadrant_=="Q") {this.quadrant_="W"; this.setMap(null); this.setMap(this.map_);}
		else if (this.quadrant_=="W") {this.quadrant_="S"; this.setMap(null); this.setMap(this.map_);}
		else if (this.quadrant_=="S") {this.quadrant_="C"; this.setMap(null); this.setMap(this.map_);}
		else if (this.quadrant_=="C") {this.quadrant_="A"; this.setMap(null);}
		else
		this.setMap(null);
      } else {
        this.setMap(this.map_);
      }
    };

	TrafficImageOverlay.prototype.refreshImg = function() {
		try {
			this.img_.src=this.image_+'?rand=' + Math.random();
		}
		catch (err) {
			Console.log(err);
		}
	}

	function initMap() {
        var map = new google.maps.Map(document.getElementById('map'), {
			zoom: 12,
			center: {lat: 22.308, lng: 114.1368}
        });

		var x0,x1,y0,y1;

		<!-- code block below is generated from awk script, showing only one block. One block for each CCTV data. -->

		y0=22.35110061;x0=114.0740287;
		y1=y0+0.04;x1=x0+0.05;
		var bounds1 = new google.maps.LatLngBounds(new google.maps.LatLng(y0,x0),new google.maps.LatLng(y1,x1));
        var srcImage1 = 'http://tdcctv.data.one.gov.hk/TC560F.JPG';
		var marker1 = new google.maps.Marker({position:new google.maps.LatLng(y0,x0),map:map,title: 'Tsing Ma Bridge'});
		var quadrant1 = "Q";
        overlay1 = new TrafficImageOverlay(bounds1, srcImage1, quadrant1, map);
		arr.push(overlay1);
		marker1.addListener('click', function() {overlay1.toggle();});

		<!-- end code block generated from awk script -->
	}

    function TrafficImageOverlay(bounds, image, quadrant, map) {this.bounds_ = bounds;this.image_ = image;this.quadrant_ = quadrant;this.map_ = map;this.div_ = null;this.setMap(map);}
		TrafficImageOverlay.prototype.onAdd = function() {
        var div = document.createElement('div');div.style.borderStyle = 'none';div.style.borderWidth = '0px';div.style.position = 'absolute';
        var img = document.createElement('img');img.src = this.image_;img.style.width = '100%';img.style.height = '100%';
		img.style.position = 'absolute';div.appendChild(img);this.div_ = div;
		var panes = this.getPanes();panes.overlayLayer.appendChild(div);
		this.img_=img;
    };

	TrafficImageOverlay.prototype.draw = function() {
        var overlayProjection = this.getProjection();
        var sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
        var ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());
        var div = this.div_;div.style.left = sw.x+'px';div.style.top=ne.y+'px';div.style.width=(ne.x-sw.x)+'px';div.style.height=(sw.y-ne.y)+'px';
		if (this.quadrant_=="Q") {
			div = this.div_;div.style.left = sw.x-(ne.x-sw.x)+'px';div.style.top=ne.y+'px';div.style.width=(ne.x-sw.x)+'px';div.style.height=(sw.y-ne.y)+'px';
		}else if (this.quadrant_=="W") {
			div = this.div_;div.style.left = sw.x+'px';div.style.top=ne.y+'px';div.style.width=(ne.x-sw.x)+'px';div.style.height=(sw.y-ne.y)+'px';
		}else if (this.quadrant_=="A") {
			div = this.div_;div.style.left = sw.x-(ne.x-sw.x)+'px';div.style.top=ne.y+(sw.y-ne.y)+'px';div.style.width=(ne.x-sw.x)+'px';div.style.height=(sw.y-ne.y)+'px';
		}else if (this.quadrant_=="S") {
			div = this.div_;div.style.left = sw.x+'px';div.style.top=ne.y+(sw.y-ne.y)+'px';div.style.width=(ne.x-sw.x)+'px';div.style.height=(sw.y-ne.y)+'px';
		}else if (this.quadrant_=="C") {
			div = this.div_;div.style.left = sw.x-(ne.x-sw.x)/2+'px';div.style.top=ne.y+(sw.y-ne.y)+'px';div.style.width=(ne.x-sw.x)+'px';div.style.height=(sw.y-ne.y)+'px';
		}
    };

	TrafficImageOverlay.prototype.onRemove = function() {
        this.div_.parentNode.removeChild(this.div_);this.div_ = null;
    };

    google.maps.event.addDomListener(window, 'load', initMap);

	var refreshIdx = 0;
	setInterval(
		function() {
			refreshIdx++;
			if (refreshIdx >= arr.length) {
				refreshIdx = 0;
			}
			try {
				arr[refreshIdx].refreshImg();
			} catch (err) {
				Console.log(err);
			}
		},10000
	);
    </script>
  </head>
  <body>
<div id="map"></div>
</body>
</html>
Advertisements

One thought on “Consolidating real time traffic CCTV data from data.gov.hk with Goolge Map API

  1. Pingback: Coordinate conversion from HK80 grid to Google Map and vice versa using Nspire | gmgolem

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s