]> dev.renevier.net Git - syp.git/blob - openlayers/lib/OpenLayers/Format/SLD/v1.js
fixes notices
[syp.git] / openlayers / lib / OpenLayers / Format / SLD / v1.js
1 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2  * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3  * full text of the license. */
4
5 /**
6  * @requires OpenLayers/Rule.js
7  * @requires OpenLayers/Format/SLD.js
8  * @requires OpenLayers/Format/Filter/v1_0_0.js
9  */
10
11 /**
12  * Class: OpenLayers.Format.SLD.v1
13  * Superclass for SLD version 1 parsers.
14  *
15  * Inherits from:
16  *  - <OpenLayers.Format.Filter.v1_0_0>
17  */
18 OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
19     
20     /**
21      * Property: namespaces
22      * {Object} Mapping of namespace aliases to namespace URIs.
23      */
24     namespaces: {
25         sld: "http://www.opengis.net/sld",
26         ogc: "http://www.opengis.net/ogc",
27         gml: "http://www.opengis.net/gml",
28         xlink: "http://www.w3.org/1999/xlink",
29         xsi: "http://www.w3.org/2001/XMLSchema-instance"
30     },
31     
32     /**
33      * Property: defaultPrefix
34      */
35     defaultPrefix: "sld",
36
37     /**
38      * Property: schemaLocation
39      * {String} Schema location for a particular minor version.
40      */
41     schemaLocation: null,
42
43     /**
44      * APIProperty: defaultSymbolizer.
45      * {Object} A symbolizer with the SLD defaults.
46      */
47     defaultSymbolizer: {
48         fillColor: "#808080",
49         fillOpacity: 1,
50         strokeColor: "#000000",
51         strokeOpacity: 1,
52         strokeWidth: 1,
53         strokeDashstyle: "solid",
54         pointRadius: 3,
55         graphicName: "square"
56     },
57     
58     /**
59      * Constructor: OpenLayers.Format.SLD.v1
60      * Instances of this class are not created directly.  Use the
61      *     <OpenLayers.Format.SLD> constructor instead.
62      *
63      * Parameters:
64      * options - {Object} An optional object whose properties will be set on
65      *     this instance.
66      */
67     initialize: function(options) {
68         OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);
69     },
70     
71     /**
72      * Method: read
73      *
74      * Parameters:
75      * data - {DOMElement} An SLD document element.
76      * options - {Object} Options for the reader.
77      *
78      * Valid options:
79      * namedLayersAsArray - {Boolean}  Generate a namedLayers array.  If false,
80      *     the namedLayers property value will be an object keyed by layer name.
81      *     Default is false.
82      *
83      * Returns:
84      * {Object} An object representing the SLD.
85      */
86     read: function(data, options) {
87         options = OpenLayers.Util.applyDefaults(options, this.options);
88         var sld = {
89             namedLayers: options.namedLayersAsArray === true ? [] : {}
90         };
91         this.readChildNodes(data, sld);
92         return sld;
93     },
94     
95     /**
96      * Property: readers
97      * Contains public functions, grouped by namespace prefix, that will
98      *     be applied when a namespaced node is found matching the function
99      *     name.  The function will be applied in the scope of this parser
100      *     with two arguments: the node being read and a context object passed
101      *     from the parent.
102      */
103     readers: OpenLayers.Util.applyDefaults({
104         "sld": {
105             "StyledLayerDescriptor": function(node, sld) {
106                 sld.version = node.getAttribute("version");
107                 this.readChildNodes(node, sld);
108             },
109             "Name": function(node, obj) {
110                 obj.name = this.getChildValue(node);
111             },
112             "Title": function(node, obj) {
113                 obj.title = this.getChildValue(node);
114             },
115             "Abstract": function(node, obj) {
116                 obj.description = this.getChildValue(node);
117             },
118             "NamedLayer": function(node, sld) {
119                 var layer = {
120                     userStyles: [],
121                     namedStyles: []
122                 };
123                 this.readChildNodes(node, layer);
124                 // give each of the user styles this layer name
125                 for(var i=0, len=layer.userStyles.length; i<len; ++i) {
126                     layer.userStyles[i].layerName = layer.name;
127                 }
128                 if(sld.namedLayers instanceof Array) {
129                     sld.namedLayers.push(layer);                
130                 } else {
131                     sld.namedLayers[layer.name] = layer;
132                 }
133             },
134             "NamedStyle": function(node, layer) {
135                 layer.namedStyles.push(
136                     this.getChildName(node.firstChild)
137                 );
138             },
139             "UserStyle": function(node, layer) {
140                 var obj = {defaultsPerSymbolizer: true, rules: []};
141                 this.readChildNodes(node, obj);
142                 var style = new OpenLayers.Style(this.defaultSymbolizer, obj);
143                 layer.userStyles.push(style);
144             },
145             "IsDefault": function(node, style) {
146                 if(this.getChildValue(node) == "1") {
147                     style.isDefault = true;
148                 }
149             },
150             "FeatureTypeStyle": function(node, style) {
151                 // OpenLayers doesn't have a place for FeatureTypeStyle
152                 // Name, Title, Abstract, FeatureTypeName, or
153                 // SemanticTypeIdentifier so, we make a temporary object
154                 // and later just use the Rule(s).
155                 var obj = {
156                     rules: []
157                 };
158                 this.readChildNodes(node, obj);
159                 style.rules = obj.rules;
160             },
161             "Rule": function(node, obj) {
162                 var rule = new OpenLayers.Rule();
163                 this.readChildNodes(node, rule);
164                 obj.rules.push(rule);
165             },
166             "ElseFilter": function(node, rule) {
167                 rule.elseFilter = true;
168             },
169             "MinScaleDenominator": function(node, rule) {
170                 rule.minScaleDenominator = parseFloat(this.getChildValue(node));
171             },
172             "MaxScaleDenominator": function(node, rule) {
173                 rule.maxScaleDenominator = parseFloat(this.getChildValue(node));
174             },
175             "TextSymbolizer": function(node, rule) {
176                 // OpenLayers doens't do painter's order, instead we extend
177                 var symbolizer = rule.symbolizer["Text"] || {};
178                 this.readChildNodes(node, symbolizer);
179                 // in case it didn't exist before
180                 rule.symbolizer["Text"] = symbolizer;
181             },
182             "Label": function(node, symbolizer) {
183                 // only supporting literal or property name
184                 var obj = {};
185                 this.readChildNodes(node, obj);
186                 if(obj.property) {
187                     symbolizer.label = "${" + obj.property + "}";
188                 } else {
189                     var value = this.readOgcExpression(node);
190                     if(value) {
191                         symbolizer.label = value;
192                     }
193                 }
194             },
195             "Font": function(node, symbolizer) {
196                 this.readChildNodes(node, symbolizer);
197             },
198             "Halo": function(node, symbolizer) {
199                 // halo has a fill, so send fresh object
200                 var obj = {};
201                 this.readChildNodes(node, obj);
202                 symbolizer.haloRadius = obj.haloRadius;
203                 symbolizer.haloColor = obj.fillColor;
204                 symbolizer.haloOpacity = obj.fillOpacity;
205             },
206             "Radius": function(node, symbolizer) {
207                 var radius = this.readOgcExpression(node);
208                 if(radius != null) {
209                     // radius is only used for halo
210                     symbolizer.haloRadius = radius;
211                 }
212             },
213             "LineSymbolizer": function(node, rule) {
214                 // OpenLayers doesn't do painter's order, instead we extend
215                 var symbolizer = rule.symbolizer["Line"] || {};
216                 this.readChildNodes(node, symbolizer);
217                 // in case it didn't exist before
218                 rule.symbolizer["Line"] = symbolizer;
219             },
220             "PolygonSymbolizer": function(node, rule) {
221                 // OpenLayers doens't do painter's order, instead we extend
222                 var symbolizer = rule.symbolizer["Polygon"] || {};
223                 this.readChildNodes(node, symbolizer);
224                 // in case it didn't exist before
225                 rule.symbolizer["Polygon"] = symbolizer;
226             },
227             "PointSymbolizer": function(node, rule) {
228                 // OpenLayers doens't do painter's order, instead we extend
229                 var symbolizer = rule.symbolizer["Point"] || {};
230                 this.readChildNodes(node, symbolizer);
231                 // in case it didn't exist before
232                 rule.symbolizer["Point"] = symbolizer;
233             },
234             "Stroke": function(node, symbolizer) {
235                 symbolizer.stroke = true;
236                 this.readChildNodes(node, symbolizer);
237             },
238             "Fill": function(node, symbolizer) {
239                 symbolizer.fill = true;
240                 this.readChildNodes(node, symbolizer);
241             },
242             "CssParameter": function(node, symbolizer) {
243                 var cssProperty = node.getAttribute("name");
244                 var symProperty = this.cssMap[cssProperty];
245                 if(symProperty) {
246                     // Limited support for parsing of OGC expressions
247                     var value = this.readOgcExpression(node);
248                     // always string, could be an empty string
249                     if(value) {
250                         symbolizer[symProperty] = value;
251                     }
252                 }
253             },
254             "Graphic": function(node, symbolizer) {
255                 symbolizer.graphic = true;
256                 var graphic = {};
257                 // painter's order not respected here, clobber previous with next
258                 this.readChildNodes(node, graphic);
259                 // directly properties with names that match symbolizer properties
260                 var properties = [
261                     "strokeColor", "strokeWidth", "strokeOpacity",
262                     "strokeLinecap", "fillColor", "fillOpacity",
263                     "graphicName", "rotation", "graphicFormat"
264                 ];
265                 var prop, value;
266                 for(var i=0, len=properties.length; i<len; ++i) {
267                     prop = properties[i];
268                     value = graphic[prop];
269                     if(value != undefined) {
270                         symbolizer[prop] = value;
271                     }
272                 }
273                 // set other generic properties with specific graphic property names
274                 if(graphic.opacity != undefined) {
275                     symbolizer.graphicOpacity = graphic.opacity;
276                 }
277                 if(graphic.size != undefined) {
278                     symbolizer.pointRadius = graphic.size / 2;
279                 }
280                 if(graphic.href != undefined) {
281                     symbolizer.externalGraphic = graphic.href;
282                 }
283                 if(graphic.rotation != undefined) {
284                     symbolizer.rotation = graphic.rotation;
285                 }
286             },
287             "ExternalGraphic": function(node, graphic) {
288                 this.readChildNodes(node, graphic);
289             },
290             "Mark": function(node, graphic) {
291                 this.readChildNodes(node, graphic);
292             },
293             "WellKnownName": function(node, graphic) {
294                 graphic.graphicName = this.getChildValue(node);
295             },
296             "Opacity": function(node, obj) {
297                 var opacity = this.readOgcExpression(node);
298                 // always string, could be empty string
299                 if(opacity) {
300                     obj.opacity = opacity;
301                 }
302             },
303             "Size": function(node, obj) {
304                 var size = this.readOgcExpression(node);
305                 // always string, could be empty string
306                 if(size) {
307                     obj.size = size;
308                 }
309             },
310             "Rotation": function(node, obj) {
311                 var rotation = this.readOgcExpression(node);
312                 // always string, could be empty string
313                 if(rotation) {
314                     obj.rotation = rotation;
315                 }
316             },
317             "OnlineResource": function(node, obj) {
318                 obj.href = this.getAttributeNS(
319                     node, this.namespaces.xlink, "href"
320                 );
321             },
322             "Format": function(node, graphic) {
323                 graphic.graphicFormat = this.getChildValue(node);
324             }
325         }
326     }, OpenLayers.Format.Filter.v1_0_0.prototype.readers),
327     
328     /**
329      * Property: cssMap
330      * {Object} Object mapping supported css property names to OpenLayers
331      *     symbolizer property names.
332      */
333     cssMap: {
334         "stroke": "strokeColor",
335         "stroke-opacity": "strokeOpacity",
336         "stroke-width": "strokeWidth",
337         "stroke-linecap": "strokeLinecap",
338         "stroke-dasharray": "strokeDashstyle",
339         "fill": "fillColor",
340         "fill-opacity": "fillOpacity",
341         "font-family": "fontFamily",
342         "font-size": "fontSize",
343         "font-weight": "fontWeight",
344         "font-style": "fontStyle"
345     },
346     
347     /**
348      * Method: getCssProperty
349      * Given a symbolizer property, get the corresponding CSS property
350      *     from the <cssMap>.
351      *
352      * Parameters:
353      * sym - {String} A symbolizer property name.
354      *
355      * Returns:
356      * {String} A CSS property name or null if none found.
357      */
358     getCssProperty: function(sym) {
359         var css = null;
360         for(var prop in this.cssMap) {
361             if(this.cssMap[prop] == sym) {
362                 css = prop;
363                 break;
364             }
365         }
366         return css;
367     },
368     
369     /**
370      * Method: getGraphicFormat
371      * Given a href for an external graphic, try to determine the mime-type.
372      *     This method doesn't try too hard, and will fall back to
373      *     <defautlGraphicFormat> if one of the known <graphicFormats> is not
374      *     the file extension of the provided href.
375      *
376      * Parameters:
377      * href - {String}
378      *
379      * Returns:
380      * {String} The graphic format.
381      */
382     getGraphicFormat: function(href) {
383         var format, regex;
384         for(var key in this.graphicFormats) {
385             if(this.graphicFormats[key].test(href)) {
386                 format = key;
387                 break;
388             }
389         }
390         return format || this.defautlGraphicFormat;
391     },
392     
393     /**
394      * Property: defaultGraphicFormat
395      * {String} If none other can be determined from <getGraphicFormat>, this
396      *     default will be returned.
397      */
398     defaultGraphicFormat: "image/png",
399     
400     /**
401      * Property: graphicFormats
402      * {Object} Mapping of image mime-types to regular extensions matching 
403      *     well-known file extensions.
404      */
405     graphicFormats: {
406         "image/jpeg": /\.jpe?g$/i,
407         "image/gif": /\.gif$/i,
408         "image/png": /\.png$/i
409     },
410
411     /**
412      * Method: write
413      *
414      * Parameters:
415      * sld - {Object} An object representing the SLD.
416      *
417      * Returns:
418      * {DOMElement} The root of an SLD document.
419      */
420     write: function(sld) {
421         return this.writers.sld.StyledLayerDescriptor.apply(this, [sld]);
422     },
423     
424     /**
425      * Property: writers
426      * As a compliment to the readers property, this structure contains public
427      *     writing functions grouped by namespace alias and named like the
428      *     node names they produce.
429      */
430     writers: OpenLayers.Util.applyDefaults({
431         "sld": {
432             "StyledLayerDescriptor": function(sld) {
433                 var root = this.createElementNSPlus(
434                     "StyledLayerDescriptor",
435                     {attributes: {
436                         "version": this.VERSION,
437                         "xsi:schemaLocation": this.schemaLocation
438                     }}
439                 );
440                 // add in optional name
441                 if(sld.name) {
442                     this.writeNode("Name", sld.name, root);
443                 }
444                 // add in optional title
445                 if(sld.title) {
446                     this.writeNode("Title", sld.title, root);
447                 }
448                 // add in optional description
449                 if(sld.description) {
450                     this.writeNode("Abstract", sld.description, root);
451                 }
452                 // add in named layers
453                 // allow namedLayers to be an array
454                 if(sld.namedLayers instanceof Array) {
455                     for(var i=0, len=sld.namedLayers.length; i<len; ++i) {
456                         this.writeNode("NamedLayer", sld.namedLayers[i], root);
457                     }
458                 } else {
459                     for(var name in sld.namedLayers) {
460                         this.writeNode("NamedLayer", sld.namedLayers[name], root);
461                     }
462                 }
463                 return root;
464             },
465             "Name": function(name) {
466                 return this.createElementNSPlus("Name", {value: name});
467             },
468             "Title": function(title) {
469                 return this.createElementNSPlus("Title", {value: title});
470             },
471             "Abstract": function(description) {
472                 return this.createElementNSPlus(
473                     "Abstract", {value: description}
474                 );
475             },
476             "NamedLayer": function(layer) {
477                 var node = this.createElementNSPlus("NamedLayer");
478
479                 // add in required name
480                 this.writeNode("Name", layer.name, node);
481
482                 // optional sld:LayerFeatureConstraints here
483
484                 // add in named styles
485                 if(layer.namedStyles) {
486                     for(var i=0, len=layer.namedStyles.length; i<len; ++i) {
487                         this.writeNode(
488                             "NamedStyle", layer.namedStyles[i], node
489                         );
490                     }
491                 }
492                 
493                 // add in user styles
494                 if(layer.userStyles) {
495                     for(var i=0, len=layer.userStyles.length; i<len; ++i) {
496                         this.writeNode(
497                             "UserStyle", layer.userStyles[i], node
498                         );
499                     }
500                 }
501                 
502                 return node;
503             },
504             "NamedStyle": function(name) {
505                 var node = this.createElementNSPlus("NamedStyle");
506                 this.writeNode("Name", name, node);
507                 return node;
508             },
509             "UserStyle": function(style) {
510                 var node = this.createElementNSPlus("UserStyle");
511
512                 // add in optional name
513                 if(style.name) {
514                     this.writeNode("Name", style.name, node);
515                 }
516                 // add in optional title
517                 if(style.title) {
518                     this.writeNode("Title", style.title, node);
519                 }
520                 // add in optional description
521                 if(style.description) {
522                     this.writeNode("Abstract", style.description, node);
523                 }
524                 
525                 // add isdefault
526                 if(style.isDefault) {
527                     this.writeNode("IsDefault", style.isDefault, node);
528                 }
529                 
530                 // add FeatureTypeStyles
531                 this.writeNode("FeatureTypeStyle", style, node);
532                 
533                 return node;
534             },
535             "IsDefault": function(bool) {
536                 return this.createElementNSPlus(
537                     "IsDefault", {value: (bool) ? "1" : "0"}
538                 );
539             },
540             "FeatureTypeStyle": function(style) {
541                 var node = this.createElementNSPlus("FeatureTypeStyle");
542                 
543                 // OpenLayers currently stores no Name, Title, Abstract,
544                 // FeatureTypeName, or SemanticTypeIdentifier information
545                 // related to FeatureTypeStyle
546                 
547                 // add in rules
548                 for(var i=0, len=style.rules.length; i<len; ++i) {
549                     this.writeNode("Rule", style.rules[i], node);
550                 }
551                 
552                 return node;
553             },
554             "Rule": function(rule) {
555                 var node = this.createElementNSPlus("Rule");
556
557                 // add in optional name
558                 if(rule.name) {
559                     this.writeNode("Name", rule.name, node);
560                 }
561                 // add in optional title
562                 if(rule.title) {
563                     this.writeNode("Title", rule.title, node);
564                 }
565                 // add in optional description
566                 if(rule.description) {
567                     this.writeNode("Abstract", rule.description, node);
568                 }
569                 
570                 // add in LegendGraphic here
571                 
572                 // add in optional filters
573                 if(rule.elseFilter) {
574                     this.writeNode("ElseFilter", null, node);
575                 } else if(rule.filter) {
576                     this.writeNode("ogc:Filter", rule.filter, node);
577                 }
578                 
579                 // add in scale limits
580                 if(rule.minScaleDenominator != undefined) {
581                     this.writeNode(
582                         "MinScaleDenominator", rule.minScaleDenominator, node
583                     );
584                 }
585                 if(rule.maxScaleDenominator != undefined) {
586                     this.writeNode(
587                         "MaxScaleDenominator", rule.maxScaleDenominator, node
588                     );
589                 }
590                 
591                 // add in symbolizers (relies on geometry type keys)
592                 var types = OpenLayers.Style.SYMBOLIZER_PREFIXES;
593                 var type, symbolizer;
594                 for(var i=0, len=types.length; i<len; ++i) {
595                     type = types[i];
596                     symbolizer = rule.symbolizer[type];
597                     if(symbolizer) {
598                         this.writeNode(
599                             type + "Symbolizer", symbolizer, node
600                         );
601                     }
602                 }
603                 return node;
604
605             },
606             "ElseFilter": function() {
607                 return this.createElementNSPlus("ElseFilter");
608             },
609             "MinScaleDenominator": function(scale) {
610                 return this.createElementNSPlus(
611                     "MinScaleDenominator", {value: scale}
612                 );
613             },
614             "MaxScaleDenominator": function(scale) {
615                 return this.createElementNSPlus(
616                     "MaxScaleDenominator", {value: scale}
617                 );
618             },
619             "LineSymbolizer": function(symbolizer) {
620                 var node = this.createElementNSPlus("LineSymbolizer");
621                 this.writeNode("Stroke", symbolizer, node);
622                 return node;
623             },
624             "Stroke": function(symbolizer) {
625                 var node = this.createElementNSPlus("Stroke");
626
627                 // GraphicFill here
628                 // GraphicStroke here
629
630                 // add in CssParameters
631                 if(symbolizer.strokeColor != undefined) {
632                     this.writeNode(
633                         "CssParameter",
634                         {symbolizer: symbolizer, key: "strokeColor"},
635                         node
636                     );
637                 }
638                 if(symbolizer.strokeOpacity != undefined) {
639                     this.writeNode(
640                         "CssParameter",
641                         {symbolizer: symbolizer, key: "strokeOpacity"},
642                         node
643                     );
644                 }
645                 if(symbolizer.strokeWidth != undefined) {
646                     this.writeNode(
647                         "CssParameter",
648                         {symbolizer: symbolizer, key: "strokeWidth"},
649                         node
650                     );
651                 }
652                 return node;
653             },
654             "CssParameter": function(obj) {
655                 // not handling ogc:expressions for now
656                 return this.createElementNSPlus("CssParameter", {
657                     attributes: {name: this.getCssProperty(obj.key)},
658                     value: obj.symbolizer[obj.key]
659                 });
660             },
661             "TextSymbolizer": function(symbolizer) {
662                 var node = this.createElementNSPlus("TextSymbolizer");
663                 // add in optional Label
664                 if(symbolizer.label != null) {
665                     this.writeNode("Label", symbolizer.label, node);
666                 }
667                 // add in optional Font
668                 if(symbolizer.fontFamily != null ||
669                    symbolizer.fontSize != null ||
670                    symbolizer.fontWeight != null ||
671                    symbolizer.fontStyle != null) {
672                     this.writeNode("Font", symbolizer, node);
673                 }
674                 // add in optional Halo
675                 if(symbolizer.haloRadius != null ||
676                    symbolizer.haloColor != null ||
677                    symbolizer.haloOpacity != null) {
678                     this.writeNode("Halo", symbolizer, node);
679                 }
680                 // add in optional Fill
681                 if(symbolizer.fillColor != null ||
682                    symbolizer.fillOpacity != null) {
683                     this.writeNode("Fill", symbolizer, node);
684                 }
685                 return node;
686             },
687             "Font": function(symbolizer) {
688                 var node = this.createElementNSPlus("Font");
689                 // add in CssParameters
690                 if(symbolizer.fontFamily) {
691                     this.writeNode(
692                         "CssParameter",
693                         {symbolizer: symbolizer, key: "fontFamily"},
694                         node
695                     );
696                 }
697                 if(symbolizer.fontSize) {
698                     this.writeNode(
699                         "CssParameter",
700                         {symbolizer: symbolizer, key: "fontSize"},
701                         node
702                     );
703                 }
704                 if(symbolizer.fontWeight) {
705                     this.writeNode(
706                         "CssParameter",
707                         {symbolizer: symbolizer, key: "fontWeight"},
708                         node
709                     );
710                 }
711                 if(symbolizer.fontStyle) {
712                     this.writeNode(
713                         "CssParameter",
714                         {symbolizer: symbolizer, key: "fontStyle"},
715                         node
716                     );
717                 }
718                 return node;
719             },
720             "Label": function(label) {
721                 // only the simplest of ogc:expression handled
722                 // {label: "some text and a ${propertyName}"}
723                 var node = this.createElementNSPlus("Label");
724                 var tokens = label.split("${");
725                 node.appendChild(this.createTextNode(tokens[0]));
726                 var item, last;
727                 for(var i=1, len=tokens.length; i<len; i++) {
728                     item = tokens[i];
729                     last = item.indexOf("}"); 
730                     if(last > 0) {
731                         this.writeNode(
732                             "ogc:PropertyName",
733                             {property: item.substring(0, last)},
734                             node
735                         );
736                         node.appendChild(
737                             this.createTextNode(item.substring(++last))
738                         );
739                     } else {
740                         // no ending }, so this is a literal ${
741                         node.appendChild(
742                             this.createTextNode("${" + item)
743                         );
744                     }
745                 }
746                 return node;
747             },
748             "Halo": function(symbolizer) {
749                 var node = this.createElementNSPlus("Halo");
750                 if(symbolizer.haloRadius) {
751                     this.writeNode("Radius", symbolizer.haloRadius, node);
752                 }
753                 if(symbolizer.haloColor || symbolizer.haloOpacity) {
754                     this.writeNode("Fill", {
755                         fillColor: symbolizer.haloColor,
756                         fillOpacity: symbolizer.haloOpacity
757                     }, node);
758                 }
759                 return node;
760             },
761             "Radius": function(value) {
762                 return node = this.createElementNSPlus("Radius", {
763                     value: value
764                 });
765             },
766             "PolygonSymbolizer": function(symbolizer) {
767                 var node = this.createElementNSPlus("PolygonSymbolizer");
768                 if(symbolizer.fillColor != undefined ||
769                    symbolizer.fillOpacity != undefined) {
770                     this.writeNode("Fill", symbolizer, node);
771                 }
772                 if(symbolizer.strokeWidth != undefined ||
773                    symbolizer.strokeColor != undefined ||
774                    symbolizer.strokeOpacity != undefined ||
775                    symbolizer.strokeDashstyle != undefined) {
776                     this.writeNode("Stroke", symbolizer, node);
777                 }
778                 return node;
779             },
780             "Fill": function(symbolizer) {
781                 var node = this.createElementNSPlus("Fill");
782                 
783                 // GraphicFill here
784                 
785                 // add in CssParameters
786                 if(symbolizer.fillColor) {
787                     this.writeNode(
788                         "CssParameter",
789                         {symbolizer: symbolizer, key: "fillColor"},
790                         node
791                     );
792                 }
793                 if(symbolizer.fillOpacity != null) {
794                     this.writeNode(
795                         "CssParameter",
796                         {symbolizer: symbolizer, key: "fillOpacity"},
797                         node
798                     );
799                 }
800                 return node;
801             },
802             "PointSymbolizer": function(symbolizer) {
803                 var node = this.createElementNSPlus("PointSymbolizer");
804                 this.writeNode("Graphic", symbolizer, node);
805                 return node;
806             },
807             "Graphic": function(symbolizer) {
808                 var node = this.createElementNSPlus("Graphic");
809                 if(symbolizer.externalGraphic != undefined) {
810                     this.writeNode("ExternalGraphic", symbolizer, node);
811                 } else {
812                     this.writeNode("Mark", symbolizer, node);
813                 }
814                 
815                 if(symbolizer.graphicOpacity != undefined) {
816                     this.writeNode("Opacity", symbolizer.graphicOpacity, node);
817                 }
818                 if(symbolizer.pointRadius != undefined) {
819                     this.writeNode("Size", symbolizer.pointRadius * 2, node);
820                 }
821                 if(symbolizer.rotation != undefined) {
822                     this.writeNode("Rotation", symbolizer.rotation, node);
823                 }
824                 return node;
825             },
826             "ExternalGraphic": function(symbolizer) {
827                 var node = this.createElementNSPlus("ExternalGraphic");
828                 this.writeNode(
829                     "OnlineResource", symbolizer.externalGraphic, node
830                 );
831                 var format = symbolizer.graphicFormat ||
832                              this.getGraphicFormat(symbolizer.externalGraphic);
833                 this.writeNode("Format", format, node);
834                 return node;
835             },
836             "Mark": function(symbolizer) {
837                 var node = this.createElementNSPlus("Mark");
838                 if(symbolizer.graphicName) {
839                     this.writeNode("WellKnownName", symbolizer.graphicName, node);
840                 }
841                 this.writeNode("Fill", symbolizer, node);
842                 this.writeNode("Stroke", symbolizer, node);
843                 return node;
844             },
845             "WellKnownName": function(name) {
846                 return this.createElementNSPlus("WellKnownName", {
847                     value: name
848                 });
849             },
850             "Opacity": function(value) {
851                 return this.createElementNSPlus("Opacity", {
852                     value: value
853                 });
854             },
855             "Size": function(value) {
856                 return this.createElementNSPlus("Size", {
857                     value: value
858                 });
859             },
860             "Rotation": function(value) {
861                 return this.createElementNSPlus("Rotation", {
862                     value: value
863                 });
864             },
865             "OnlineResource": function(href) {
866                 return this.createElementNSPlus("OnlineResource", {
867                     attributes: {
868                         "xlink:type": "simple",
869                         "xlink:href": href
870                     }
871                 });
872             },
873             "Format": function(format) {
874                 return this.createElementNSPlus("Format", {
875                     value: format
876                 });
877             }
878         }
879     }, OpenLayers.Format.Filter.v1_0_0.prototype.writers),
880     
881     CLASS_NAME: "OpenLayers.Format.SLD.v1" 
882
883 });