woensdag 23 juni 2010

Anchor Renderer

So as I wrote in an earlier post, for the GEOZET viewer the features in the map need to be accessible by keyboard, that's why we ended up using anchors. The anchors are styled using a css class coming from a feature attribute.

So the HTML I had to create using the renderer is for the normal features an anchor with a css class to map to the background-image, the anchor is absolutely positioned. The innerHTML of the anchor has the internal id used also in the print list, it won't be visible in the map only when printing. The identifiers (id attributes) of the anchors are the feature ids generated by OpenLayers. Since background-image print can be troublesome, we decided to print white blocks with the number in it, corresponding to a number in a list underneath the map. Using only border color to differentiate the feature classes.

For clusters we needed a different HTML structure, since the number of features present in the cluster needed to be displayed. It is an anchor with a span, the span contains the internal ID. The anchor also has a child of type "strong" with the number of features in the cluster in it. This is used as the label.

The first issue I ran into was that all renderers coupled to an OpenLayers.Layer.Vector needed to be in the OpenLayers namespace, which is the subject of:

http://trac.openlayers.org/ticket/2669

I had some trouble deciding whether or not my custom renderer needed to extend OpenLayers.Renderer.Elements or OpenLayers.Renderer. In the end I decided on the latter, mostly since the only overlap was the getFeatureIdFromEvent function, and also since I needed to put an _style object on all the nodes to overcome destroy issues in IE.

The drawText function is used to add labels for the cluster features, and it looks something like:


Since I incrementally implemented my Renderer, I had a few situations in which strange things were happening, for instance using the cluster strategy, I was getting more and more features on my map, until I realized that I had not yet implemented eraseGeometry in my Renderer. One of the problems I had when implementing the renderer is the fact that eraseGeometry does not get the featureId, and I needed to get easy access to the DOM structure associated with the feature, using the featureId and not the geometry id. This is subject of:

http://trac.openlayers.org/ticket/2693

Chris Schmidt already answered on the e-mail list that he has no objection to this change. This would also be a benefit for the existing Canvas Renderer of OpenLayers, since it does not need to keep a node hashmap mapping featureIds to geometryIds anymore.

My implementation of eraseGeometry ended up to be very simple:


I did not need to implement removeText, since in my Renderer the labels are part of the DOM structure of the feature, so it will automatically be cleared when the geometry is erased.

The drawGeometry function ended up to be the most difficult. It took some time for me to realize that if the style object passed has a display property of "none", that the Renderer should not draw the feature, or even remove it from the DOM if it exists. The current code is something like (this still needs to be cleaned up so bear with me):


Goes to say lastly that this Renderer only supports point geometries. This could be a start to replace the Marker layer maybe in the future, I am not sure.

The vector layer definition is now:


Here is a screenshot of the print function (the administrative list with the numbers corresponding to the features in the map follows on page 2, not included in this screendump), we still need to find a way to get a good internal number for the features, maybe by having the Web Feature Service sort them by the distance from the center:


vrijdag 18 juni 2010

Custom Renderer in OpenLayers

For a new project for the Dutch government (the project is run by Geonovum) we are creating a viewer based on OpenLayers. The project is called GEOZET and it is an abbreviation in Dutch. In English it means something like "geographical view and search engine". We in the above sentence is a team of developers in Java, Javascript and HTML/CSS. I am focussing on the javascript part together with someone else.

The requirements are quite tough, and they deal a lot with accessibility.

So using the keyboard people must be able to step through the features in the map, which kind of rules out the standard renderers available in OpenLayers. That's why I ended up creating my own renderer which creates anchors, with a lot of help from Maarten van Oudenniel (the HTML/CSS guru in the project team) who came up with the actual HTML structure which I have to render and the corresponding CSS. The structure differs for normal features and for clustered features since for clusters there is a label with the amout of subfeatures in the cluster.

Maarten also came up with a good way to print such a map using normal browser print, the features in the map will become white squares with a number, and below the map a list will be shown with the actual information from the features. Something I had never thought about doing. Downside is that next to the feature structure in the map, we also have to keep an unordered list synchronized which will only be shown when printing. But this is because no separate print button is allowed in the application.

In a future post I'll tell you more about the internals of the renderer.