Friday, October 26, 2012

How To Dynamically Set The Radius Of A Feature Point To Reflect The Spacial Dimension In OpenLayers

If you draw a feature point in OpenLayers it keeps in ever zoom level the same size. Like the one on the right site shown in the map below.
Wouldn't it be nice to to give the feature point a physical size that reflects its spacial dimension in relation to the map?
This is done with the point on the left side. Zoom in or out and you will see the difference.


Hier is how its done.
1:  var map, layerOsm, layerVector;  
2:  var lonLatFrankfurt = new OpenLayers.LonLat(966375, 6466128);  
3:    
4:  function initOpenLayers() {  
5:         
6:       map = new OpenLayers.Map('map');  
7:         
8:       layerOsm = new OpenLayers.Layer.OSM("Simple OSM Map");  
9:       map.addLayer(layerOsm);  
10:         
11:         
12:       var layerVectorNormal = new OpenLayers.Layer.Vector("layer normal");  
13:       map.addLayer(layerVectorNormal);  
14:         
15:       var featureOffenbach = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(976375, 6466128));  
16:       layerVectorNormal.addFeatures([featureOffenbach]);  
17:         
18:       var style = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style.default);  
19:       OpenLayers.Util.extend(style, {pointRadius: '${calculateRadius}'});  
20:         
21:       var styleArea = new OpenLayers.Style(  
22:                 style  
23:            ,  
24:            {  
25:                 context: {  
26:                   calculateRadius: function(feature) {  
27:                           return feature.attributes.radius/feature.layer.map.getResolution();  
28:                   }  
29:                 }  
30:             }  
31:       );  
32:    
33:       var sm = new OpenLayers.StyleMap({  
34:            'default': styleArea  
35:       });  
36:         
37:         
38:       layerVector = new OpenLayers.Layer.Vector("layer spacial dimensioned", { styleMap: sm });  
39:       map.addLayer(layerVector);  
40:    
41:       var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(lonLatFrankfurt.lon, lonLatFrankfurt.lat), {radius: 7000});  
42:       layerVector.addFeatures([feature]);  
43:         
44:       map.setCenter(  
45:            lonLatFrankfurt,  
46:            10  
47:       );  
48:  }  
And now the explanation:
In line 41 we create a point feature with the additional property radius that holds the radius of the point in meter.
 OpenLayers.Feature.Vector(geometry, attributes)  
The attributes will be mapped to the attributes property of the vector object.

Than we create our Style with
 OpenLayers.Style(style, options)  
The parameter style ist an anonymous object of key-value-pairs that is also refered to as symbolizer. A list of valid properties is defined in OpenLayers.Feature.Vector.

We want to extend the original style and just change the pointRadius property. In line 18: we extend the the original default symbolizer.
 OpenLayers.Util.extend(destination, source)   
This function copies all properties from source to destination.

In line 19: we do something that is called attribute replacement. With the expression:
 ${attribute_name}  
With this expression, we can reference any property in the attributes property of the vector object. This would be a static value that doesn't change dynamically with the zoom level.

But we can also refer to a function. The functions get looked up in the feature property context.

In line 21: we create a new OpenLayers.Style with our extendes default symbolizer and the context property that holds the function to calculate the dynamic radius.
This function in line 26: gets called every time the feature is drawn and the pointRadius gets evaluated. As a single parameter it gets the feature.
Here we can set the radius in relation with the zoom level of the map and calculate the displayed pointRadius.