The MapAPI 1.0 is deprecated. Please use the new MapAPI 1.1.





« Previous chapter 10: Multi-Language support   Back to Index

Chapter 11: Drawing with SVG and VML

All modern desktop browsers have buildin support for vector graphics. Internet Explorer versions up to 8 use VML (Vector Markup Language), all other browsers and newer Internet Explorer versions use SVG (Scalable Vector Graphics). Both formats are XML based and have similar features, but are not compatible.

To support vector cross-browser graphics in your map application, the API provides the classes IWGraphicsRenderer and IWMapRenderer. An IWGraphicsRenderer uses a HTML container like a div as a canvas and is pixel based. You could use it to draw a circle at position (100, 50) with a radius of 100px. However, map applications often are used to draw polygons, lines, circles etc. in the map.

Let's assume you want to display a circle with a radius of 2 km at a specified coordinate. This is what the IWMapRenderer was designed for: You specify polygon points by coordinates and radiuses in kilometers. Everything else like projecting these coordinates to the map and repositioning after moving and zooming the map is handled by the IWMapRenderer.

The first part of this chapter shows how to use an IWGraphicsRenderer without a map and shows the basics like lines, circles and so on. The second part explains how to use an IWMapRenderer to visualize e.g. polylines with your favourite bike tours on a map.

11.1 Creating a graphic renderer

To get started, you just have to create a new IWGraphics object. This returns a renderer suitable for your browser (SVG if supported, otherwise VML). The constructor of the graphics class requires a DIV container as parameter. This container tells the graphic renderer where it has to draw the elements on the screen.

11.2 Basis shapes

First of all, VML and SVG are both very powerful and provide a set of different shapes. Our graphic renderer supports only the basis shapes. The reason for this is, that it is very difficult to implement a common interface to support all VML and SVG shapes and properties at the same time. The following shapes are supported by our renderer: LINE, CIRCLE, ELLIPSE, RECTANGLE, ROUNDED RECTANGLE, POLYLINE and POLYGON.

11.2.1 Draw a line

The following example uses thedrawLine method from the graphics object to draw the four lines below. The first and second parameter define the start and end point of the line. The other both parameters specify the width of the line and its color.

 Example 11a:

			//********************************************************************
			//* Creates a graphics renderer for the specified DOM container and
			//* draws some lines on it.
			//********************************************************************
		
			var renderer = new IWGraphics(document.getElementById('drawLineContainer'));	
			renderer.drawLine(new IWPoint(0, 10), new IWPoint(500, 10), 1, '#000000');			
			renderer.drawLine(new IWPoint(0, 15), new IWPoint(500, 15), 3, '#FF0000');
			renderer.drawLine(new IWPoint(0, 23), new IWPoint(500, 23), 5, '#00FF00');
			renderer.drawLine(new IWPoint(0, 32), new IWPoint(500, 32), 7, '#0000FF');			
		

11.2.2 Draw a circle

The following example uses the drawCircle method from the graphics object to draw some circles. The first parameter defines the center and the second parameter the radius of the circle. The other parameters specify the width and the color of the outer line and the color of the inner circle.

 Example 11b:

			//********************************************************************
			//* Creates a graphics renderer for the specified DOM container and
			//* draws some circles on it.
			//********************************************************************
		
			var graphics = new IWGraphics(document.getElementById('drawCircleContainer'));	
			graphics.drawCircle(new IWPoint( 25, 45), 20, 1, '#000000', '#0000FF');			
			graphics.drawCircle(new IWPoint( 85, 50), 10, 1, '#000000', '#00FF00');
			graphics.drawCircle(new IWPoint(145, 50), 40, 1, '#000000', '#FF0000');
			graphics.drawCircle(new IWPoint(225, 50), 30, 1, '#000000', '#00FF00');
		

11.2.3 Draw an ellipse

The following example uses the drawEllipse method from the graphics object to draw the ellipses below. The first parameter specifies the center of the ellipse and the second the x and y expansion. The last parameters specify the border width and the fill and line color of the ellipse.

 Example 11c:

			//********************************************************************
			//* Creates a graphics renderer for the specified DOM container and
			//* draws some ellipses on it.
			//********************************************************************
		
			var graphics = new IWGraphics(document.getElementById('drawEllipseContainer'));	
			graphics.drawEllipse(new IWPoint( 25, 45), new IWSize(20, 15), 1, '#000000', '#0000FF');			
			graphics.drawEllipse(new IWPoint( 85, 50), new IWSize(20, 10), 1, '#000000', '#00FF00');
			graphics.drawEllipse(new IWPoint(145, 50), new IWSize(20, 40), 1, '#000000', '#FF0000');
			graphics.drawEllipse(new IWPoint(225, 50), new IWSize(30, 40), 1, '#000000', '#00FF00');
		

11.2.4 Draw a rectangle

The following example uses the drawRectangle method from the graphics object to draw some rectangles. The first parameter specifies the upper left corner of the rectangle. The second parameter defines the size of the rectangle. You can set the with of the line, the line- and fill color by setting the last three parameters.

 Example 11d:

			//********************************************************************
			//* Creates a graphics renderer for the specified DOM container and
			//* draws some rectangles on it.
			//********************************************************************
		
			var renderer = new IWGraphics(document.getElementById('drawRectangleContainer'));	
			renderer.drawRectangle(new IWPoint(  0, 25), new IWSize( 40, 40), 1, '#000000', '#0000FF');			
			renderer.drawRectangle(new IWPoint( 60, 15), new IWSize( 20, 60), 1, '#000000', '#00FF00');
			renderer.drawRectangle(new IWPoint(100, 35), new IWSize(100, 20), 1, '#000000', '#FF0000');
			renderer.drawRectangle(new IWPoint(220, 25), new IWSize( 40, 50), 1, '#000000', '#00FF00');
		

11.2.5 Draw a rounded rectangle

The following example uses the drawRoundedRectangle method from the graphics object to draw some rounded rectangles. The first parameter specifies the upper left corner of the rectangle. The next parameters define the radius of the edges and the size of the rectangle. You can set the with of the line, the line- and fill color by setting the last parameters.

 Example 11e:

			//********************************************************************
			//* Creates a graphics renderer for the specified DOM container and
			//* draws some rounded rectangles on it.
			//********************************************************************
		
			var renderer = new IWGraphics(document.getElementById('drawRoundedRectangleContainer'));	
			renderer.drawRoundedRectangle(new IWPoint(  0, 25),  5, new IWSize( 40, 40), 1, '#000000', '#0000FF');			
			renderer.drawRoundedRectangle(new IWPoint( 60, 15),  5, new IWSize( 20, 60), 1, '#000000', '#00FF00');
			renderer.drawRoundedRectangle(new IWPoint(100, 35), 10, new IWSize(100, 20), 1, '#000000', '#FF0000');
			renderer.drawRoundedRectangle(new IWPoint(220, 25), 10, new IWSize( 40, 50), 1, '#000000', '#00FF00');
		

11.2.6 Draw a polyline

Now we use the drawPolyline method from the graphics object to draw our polylines. The first parameter specifies the points of the line. The points are given as a formatted string, where each point is separated with a whitespace. The last two parameters set the width and the line color.

 Example 11f:

			//********************************************************************
			//* Creates a graphics renderer for the specified DOM container and
			//* draws some polylines on it.
			//********************************************************************
		
			var renderer = new IWGraphics(document.getElementById('drawPolylineContainer'));	
			renderer.drawPolyline('10,10 20,20 30,10 40,20 50,10 60,20 70,10 80,20', 6, '#0000FF');			
			renderer.drawPolyline('10,40 20,50 30,40 40,50 50,40 60,50 70,40 80,50', 4, '#00FF00');
			renderer.drawPolyline('10,70 20,80 30,70 40,80 50,70 60,80 70,70 80,80', 2, '#FF0000');

			renderer.drawPolyline('100,10 140,10 140,90 180,90 180,10 220,10 220,90 260,90 260,10 300,10 300,90 340,90 340,10 380,10', 10, '#00FF00');
		

11.2.7 Draw a polygon

Now we use the drawPolygon method from the graphics object to draw our polygons. The first parameter specifies the points for the line. The points are given as a formated string, where each point is separated with a whitespace. The last three parameters set the width, the line color and the fill color.

 Example 11g:

			//********************************************************************
			//* Creates a graphics renderer for the specified DOM container and
			//* draws some polygons on it.
			//********************************************************************
		
			var renderer = new IWGraphics(document.getElementById('drawPolygonContainer'));	
			renderer.drawPolygon('10,10 70,70 10,70 70,10', 1, '#000000', '#0000FF');			
			renderer.drawPolygon('110,10 170,10 170,70 110,70', 1, '#000000', '#00FF00');
			renderer.drawPolygon('210,70 250,10 290,70', 1, '#000000', '#FF0000');
			renderer.drawPolygon('320,30 360,30 360,10 390,40 360,70 360,50, 320,50', 1, '#000000', '#0000FF');
		

11.3 Working with SVG and VML

Before the graphic objects are drawn, they are applied to the DOM. Each drawing method returns a graphic object which can be used e.g. to catch DOM events. So, they can be handled as normal DOM nodes. The returned objects are platform dependent graphic objects. These means, that you receive a VML node if you are using the Internet Explorer otherwise you get a SVG node. The example below shows graphic objects, which can be dragged around. In this case we can not use the IWDraggable interface, because the graphic object does not support the CSS attributes style.left and style.top for positioning the element. We have to call the move method of the renderer by ourself. Just take the graphics and move them around to see how the example works.

We set the transparency of each circle to 75 percent and we use a pointer as cursor to demonstrate that you can also use common style attributes.

 Example 11h:

 Source code example 11h:

			var onMouseMoveListener = null;
			var onMouseUpListener = null;	
			var mouseAdapter = new IWMouseAdapter();
			var colors = new Array('#FF0000', '#00FF00', '#0000FF');
		
			//********************************************************************
			//* Gets the drawing and dragging container.
			//********************************************************************
			
			var drawContainer = document.getElementById('drawContainer');

			//********************************************************************
			//* Creates a graphic renderer with the specified drawing container.
			//********************************************************************
			
			var renderer = new IWGraphics(drawContainer);
			
			for (var i=0; i < 5; i++)
			{
				var width = iw.random(10, 50);
				var point = new IWPoint(iw.random(0, 500), iw.random(0, 350));				
				var size = new IWSize(width, width);
					
				var myEllipse = renderer.drawEllipse(point, size, 1, '#000000', colors[iw.random(0,2)]);
				myEllipse.style.cursor = 'pointer';
				myEllipse.style.opacity = '0.75';
				myEllipse.style.filter = 'alpha(opacity=75)';

				IWEventManager.addDomListener(myEllipse, 'onmousedown', function(event) { startDragging(this, event); }.iwclosure(myEllipse)); 
			}	
			
			/**
			 * Prepares the graphic object for dragging.
			 * @private
			 * @param {Object} object the platform specific graphic object
			 * @param {MouseEvent} event the mouse event.
			 * @return {void}
			 */
			function startDragging(object, event)
			{
				iw.noPropagation(event);
				
				IWEventManager.removeListener(onMouseUpListener);

				onMouseMoveListener = IWEventManager.addDomListener(drawContainer, 'onmousemove', function(event) { drag(object, event); });
				onMouseUpListener = IWEventManager.addDomListener(drawContainer, 'onmouseup', function(event) { stopDragging(event); });
			}	

			/**
			 * Drags the graphic object to its new position.
			 * @private
			 * @param {Object} object the platform specific graphic object
			 * @param {MouseEvent} event the mouse event.
			 * @return {void}
			 */
			function drag(object, event)
			{
				iw.noPropagation(event);
				
				var mouseEvent = mouseAdapter.toMouseEvent(event, drawContainer);
				renderer.moveToTop(object);
				renderer.move(object, mouseEvent.position);
			}

			/**
			 * Releases the mousemove listener to stop dragging.
			 * @private
			 * @param {MouseEvent} event the mouse event.
			 * @return {void}
			 */
			function stopDragging(event)
			{				
				iw.noPropagation(event);
				IWEventManager.removeListener(onMouseMoveListener);
			}	
		

11.4 Drawing on the map

The previous examples demonstrate how to use the renderer to create vector graphics in a container. Now we want to draw to a map. Let's assume you have a set of coordinates (e.g. your last bike tour) and want to display them as a polyline. The method IWGraphicsRenderer.drawPolyline() is pixel based. As the coordinates in our example are Mercator they have to be projected to pixels. This step has to be repeated every time the map is zoomed. This is realized by a onzoomstart handler hiding the vector data and a onzoomend handler to reproject and redraw the data.

Warning: Please do not confuse the following example with the visualization of a route. The coordinates returned by the routing client are for the driving directions only. At the moment you cannot access the coordinates of the complete route.

 Example 11i (view example):

 Source code example 11i:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html>
	<head>
		<title>infoware mapsuite Javascript API - Chapter 11: Drawing with SVG and VML</title>
		<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
		<script type="text/javascript" src="http://iw.mapandroute.de/MapAPI-1.0/js/mapping.js"></script>
		<script type="text/javascript">

			var map = null;
			var drawingContainer = null;
			var renderer = null;
			var shape = null;
			var meterCoords = ('794922,6568639 794734,6568868 794602,6568770 794478,6568682 794407,6568788 794291,6568963 ' +
				'794276,6568984 794169,6569159 794102,6569249 794031,6569352 793983,6569423 793917,6569519 793837,6569601 ' + 
				'793793,6569664 793745,6569745 793633,6569931 793519,6570115 793630,6570192 793726,6570280 793788,6570200').split(' ');

			var startMarker;
			var endMarker;

			function initialize() 
			{				
				map = new IWMap(document.getElementById('map'));

				drawingContainer = map.getDrawingContainer();		
				renderer = new IWGraphics(drawingContainer);
				
				startMarker = new IWMarker(map, new IWCoordinate(794922, 6568639, IWCoordinate.MERCATOR));
				startMarker.setDefaultIcon(new IWIcon('http://iw.mapandroute.de/MapAPI-1.0/img/symbols/house/house_green_26.gif', new IWPoint(15, 13), new IWSize(30, 26)));
				map.getOverlayManager().getLayer(0).addOverlay(startMarker);

				endMarker = new IWMarker(map, new IWCoordinate(793788, 6570200, IWCoordinate.MERCATOR));
				endMarker.setDefaultIcon(new IWIcon('http://iw.mapandroute.de/MapAPI-1.0/img/symbols/house/house_blue_26.gif', new IWPoint(15, 13), new IWSize(30, 26)));
				map.getOverlayManager().getLayer(0).addOverlay(endMarker);

				IWEventManager.addListener(map, 'onzoomstart', hideRoute);
				IWEventManager.addListener(map, 'onzoomend', drawRoute);

				map.setCenter(new IWCoordinate(794920, 6568662, IWCoordinate.Mercator), 14);
			}

			/**
			 * Hides the route.
			 */
			function hideRoute()
			{
				//********************************************************************
				//* Removes all svg/vml nodes from the drawingContainer.
				//********************************************************************	
				
				if (shape != null)
				{
					iw.remove(shape);
				}
			}	

			/**
			 * (Re)draws the route.
			 */
			function drawRoute()
			{
				hideRoute();

				var offsetX = parseInt(drawingContainer.parentNode.style.left);
				var offsetY = parseInt(drawingContainer.parentNode.style.top);

				//********************************************************************
				//* (Re)draws the svg/vml nodes
				//********************************************************************	
				
				var points = '';
				var projection = map.getCurrentMapType().getProjection();
						
				var minx = null;
				var maxx = null;
				var miny = null;
				var maxy = null;

				var pixelArray = new Array();

				//********************************************************************
				//* Calculates the relative pixel position to the upper left corner
				//* of the map and the height and width of the drawing area.
				//********************************************************************	

				for (var i=0; i < meterCoords.length; i++)
				{
					var meterCoord = meterCoords[i].split(',');
					var pixelXY = projection.meterToPixelXY(parseFloat(meterCoord[0]), parseFloat(meterCoord[1]));

					pixelXY.x = parseInt(pixelXY.x);
					pixelXY.y = parseInt(pixelXY.y);
							
					if (minx == null || pixelXY.x < minx)
					{
						minx = pixelXY.x;
					}
					if (maxx == null || pixelXY.x > maxx)
					{
						maxx = pixelXY.x;
					}
					if (miny == null || pixelXY.y < miny)
					{
						miny = pixelXY.y;	
					}
					if (maxy == null || pixelXY.y > maxy)
					{
						maxy = pixelXY.y;
					}
				
					pixelArray.push(pixelXY);
				}

				//********************************************************************
				//* Adds a small border around the VML area
				//********************************************************************	

				minx = Math.round(minx - 20);
				maxx = Math.round(maxx + 20);
				miny = Math.round(miny - 20);
				maxy = Math.round(maxy + 20);

				//********************************************************************
				//* Creates the polyline string for the renderer. 
				//********************************************************************	

				for (var i=0; i < pixelArray.length; i++)
				{
					points += (pixelArray[i].x - minx) + ',' + (pixelArray[i].y  - miny) + ' ';
				}

				var stokeWidth = 4;
				
				if (map.getZoom() > 16)
					stokeWidth = 10;
				else if (map.getZoom() > 14)
					stokeWidth = 8;
				else if (map.getZoom() > 12)
					stokeWidth = 6;

				shape = renderer.drawPolyline(points, stokeWidth, '#00ff00');
				shape.style.opacity = '0.50';
				shape.style.filter = 'alpha(opacity=50)';
				shape.parentNode.style.position = 'absolute';
				shape.parentNode.style.left = (-offsetX + minx) + 'px';				
				shape.parentNode.style.top = (-offsetY + miny) + 'px';
				shape.parentNode.style.width = parseInt(maxx - minx) + 'px';
				shape.parentNode.style.height = parseInt(maxy - miny) + 'px';
			}
		</script>
	</head>
	<body onload="initialize();">
		<div id="map" style="background-color: #dddddd; width: 450px; height: 300px"></div>
	</body>
</html>
		

« Previous chapter 10: Multi-Language support   Back to Index