diff --git a/gmap.module b/gmap.module
index 67d390a..4df7f9d 100644
--- a/gmap.module
+++ b/gmap.module
@@ -559,6 +559,11 @@ function gmap_sanitize(&$gmap){
  *
  */
 function gmap_parse_macro($instring,$ver=2) {
+  static $multi_props = array(
+    'points','markers','feed', // we already had those
+    'circle', 'rpolygon', 'polygon', // new ones for shapes
+  );
+  foreach ($multi_props as $key) $mprops[$key] = 1;
 
   // Remove leading and trailing tags
   if (substr(trim($instring),-1)==']') {
@@ -575,11 +580,13 @@ function gmap_parse_macro($instring,$ver=2) {
     if (strlen(trim($row)) > 0) {
       $r = explode('=',$row);
       // Things that can appear multiple times
-      if (in_array(trim($r[0]),array('points','markers','feed'))) {
-        $m[trim($r[0])][] = trim($r[1]);
+      //if (in_array(trim($r[0]),array('points','markers','feed'))) {
+      $key = trim($r[0]);
+      if (isset($mprops[$key])) {
+        $m[$key][] = trim($r[1]);
       }
       else {
-        $m[trim($r[0])] = trim($r[1]);
+        $m[$key] = trim($r[1]);
       }
     }
   }
@@ -631,6 +638,58 @@ function gmap_parse_macro($instring,$ver=2) {
   unset($m['points']);
   unset($m['markers']);
 
+  // all shapes in 1 array
+  if ($m['circle']) {
+    foreach ($m['circle'] as $shape) {
+      $s = array('type' => 'circle');
+      $cp = strpos($shape, ':');
+      if ($cp !== false) {
+        $stylestr = substr($shape, 0, $cp);
+        $s['style'] = explode('/', $stylestr);
+        $shape = substr($shape, $cp+1);
+      }
+      $tmp = explode('+', $shape);
+      $s['radius'] = $tmp[1] ? ($tmp[1] * 1000) : 10000;
+      if ($tmp[2]) $s['numpoints'] = trim($tmp[2]);
+      $tmp = _gmap_str2points($tmp[0]);
+      $s['center'] = $tmp[0];
+      $m['shapes'][] = $s;
+    }
+    unset($m['circle']);
+  }
+  if ($m['rpolygon']) {
+    foreach ($m['rpolygon'] as $shape) {
+      $s = array('type' => 'rpolygon');
+      $cp = strpos($shape, ':');
+      if ($cp !== false) {
+        $stylestr = substr($shape, 0, $cp);
+        $s['style'] = explode('/', $stylestr);
+        $shape = substr($shape, $cp+1);
+      }
+      $tmp = explode('+', $shape);
+      if ($tmp[2]) $s['numpoints'] = trim($tmp[2]);
+      $tmp = _gmap_str2points($shape);
+      $s['center'] = $tmp[0];
+      $s['point2'] = $tmp[1];
+      $m['shapes'][] = $s;
+    }
+    unset($m['rpolygon']);
+  }
+  if ($m['polygon']) {
+    foreach ($m['polygon'] as $shape) {
+      $s = array('type' => 'polygon');
+      $cp = strpos($shape, ':');
+      if ($cp !== false) {
+        $stylestr = substr($shape, 0, $cp);
+        $s['style'] = explode('/', $stylestr);
+        $shape = substr($shape, $cp+1);
+      }
+      $s['points'] = _gmap_str2points($shape);
+      $m['shapes'][] = $s;
+    }
+    unset($m['polygon']);
+  }
+
   // Version 1 -> 2 conversion
   if ($ver==1) {
     // Zoom is flipped
@@ -1331,6 +1390,11 @@ function theme_gmap($element) {
     drupal_add_js($path . 'markerloader_static.js');
   }
 
+  if ($map['shapes']) {
+    drupal_add_js($path . 'shapeloader_static.js');
+    drupal_add_js($path . 'gmap_shapes.js');
+  }
+
   if (is_array($map['feed'])) {
     drupal_add_js($path . 'markerloader_georss.js');
   }
diff --git a/js/gmap_shapes.js b/js/gmap_shapes.js
new file mode 100644
index 0000000..bcf0e5c
--- /dev/null
+++ b/js/gmap_shapes.js
@@ -0,0 +1,54 @@
+/**
+ * GMap Shapes
+ * GMap API version / Base case
+ */
+/* $Id$ */
+
+Drupal.gmap.addHandler('gmap', function(elem) {
+  var obj = this;
+/*
+  obj.bind('init',function() {
+    if (obj.vars.behavior.autozoom) {
+      obj.bounds = new GLatLngBounds(new GLatLng(obj.vars.latitude,obj.vars.longitude),new GLatLng(obj.vars.latitude,obj.vars.longitude));
+    }
+  });
+*/
+  obj.bind('addshape',function(shape) {
+    //var m = new GMarker(new GLatLng(marker.latitude,marker.longitude),marker.opts);
+    pa = []; // point array (array of GLatLng-objects)
+    if (shape.type == 'circle') {
+      shape.center = new GLatLng(shape.center.latitude, shape.center.longitude);
+      pa = obj.poly.calcPolyPoints(shape.center, shape.radius, shape.numpoints);
+    }
+    else if (shape.type == 'rpolygon') {
+      shape.center = new GLatLng(shape.center.latitude, shape.center.longitude);
+      shape.point2 = new GLatLng(shape.point2.latitude, shape.point2.longitude);
+      var radius = shape.center.distanceFrom(shape.point2);
+      pa = obj.poly.calcPolyPoints(shape.center, radius, shape.numpoints);
+    }
+    else if(shape.type == 'polygon') { // CHECK: ??? always shape.type == 'polygon' here ???
+      for(i = 0; i < shape.points.length; i++) {
+        pp = shape.points[i];
+        pa.push(new GLatLng(pp.latitude, pp.longitude));
+      }
+    }
+    var sa = (shape.style) ? shape.style : [];
+    // GPolygon(points, strokeColor?, strokeWeight?, strokeOpacity?,
+    //          fillColor?,  fillOpacity?)
+    shape.shape = new GPolygon(pa, sa[0], sa[1], sa[2], sa[3], sa[4]);
+    obj.map.addOverlay(shape.shape);
+    //if (obj.vars.behavior.autozoom) {
+    //  obj.bounds.extend(marker.marker.getPoint());
+    //  obj.map.setCenter(obj.bounds.getCenter(),obj.map.getBoundsZoomLevel(obj.bounds));
+    //}
+  });
+
+  obj.bind('delshape',function(shape) {
+    obj.map.removeOverlay(shape.shape);
+  });
+
+  obj.bind('clearshapes',function() {
+    // @@@ Maybe don't nuke ALL overlays?
+    obj.map.clearOverlays();
+  });
+});
diff --git a/js/poly.js b/js/poly.js
index f865c86..9e1a98b 100644
--- a/js/poly.js
+++ b/js/poly.js
@@ -33,6 +33,31 @@ Drupal.gmap.map.prototype.poly.computeCircle = function(obj,center,point2) {
   return points;
 };
 
+Drupal.gmap.map.prototype.poly.calcPolyPoints = function(center, radM, numPoints, sAngle) {
+  if(!numPoints) numPoints = 32;
+  if(!sAngle) sAngle = 0;
+
+  var d2r = Math.PI / 180.0;
+  var r2d = 180.0 / Math.PI;
+  var angleRad = sAngle * d2r;
+  // earth semi major axis is about 6378137 m
+  var latScale = radM / 6378137 * r2d;
+  var lngScale = latScale / Math.cos(center.lngRadians())
+
+  var angInc = 2 * Math.PI / numPoints;
+  var points = [];
+  for (var i = 0; i < numPoints; i++) {
+    var lat = parseFloat(center.lat()) + latScale * Math.sin(angleRad);
+    var lng = parseFloat(center.lng()) + lngScale * Math.cos(angleRad);
+    points.push(new GLatLng(lat, lng));
+    angleRad += angInc;
+  }
+
+  // close the shape and return it
+  points.push(points[0]);
+  return points;
+}
+
 /**
  * Circle -- on screen.
  */
diff --git a/js/shapeloader_static.js b/js/shapeloader_static.js
new file mode 100644
index 0000000..23afc02
--- /dev/null
+++ b/js/shapeloader_static.js
@@ -0,0 +1,26 @@
+/**
+ * GMap Shape Loader
+ * Static Shapes.
+ * This is a simple marker loader to read markers from the map settings array.
+ * Commonly used with macros.
+ */
+/* $Id */
+
+// Add a gmap handler
+Drupal.gmap.addHandler('gmap', function(elem) {
+  var obj = this;
+  var shape, i;
+  if (obj.vars.shapes) {
+    // Inject shapes as soon as the icon loader is ready.
+    obj.bind('iconsready',function() {
+      for (i=0; i<obj.vars.shapes.length; i++) {
+        shape = obj.vars.shapes[i];
+        if (!shape.opts) shape.opts = {};
+        // TODO: style props?
+        // And add it.
+        obj.change('addshape',-1,shape);
+      }
+      obj.change('shapesready',-1);
+    });
+  }
+});
