private LineSymbolizer CreateLineSymbolizer(NodePropertyValue[] properties)
    {
      LineSymbolizer symLine = new LineSymbolizer();
      symLine.Stroke.LineCap = System.Drawing.Drawing2D.LineCap.Flat;
      symLine.Stroke.LineJoin = System.Drawing.Drawing2D.LineJoin.Miter;
      symLine.Clip = true;

      GeometryTransformInfo geomTrans = new GeometryTransformInfo();
      NodePropertyValue pv = null;

      try
      {
        int nProps = properties.Length;
        for (int i = 0; i < nProps; i++)
        {
          pv = properties[i];

          switch (pv.Name)
          {
            case "line-width":
              symLine.Stroke.Width = Convert.ToSingle(pv.Value);
              break;
            case "line-color":
              Color clr = ColorUtility.FromHtml(pv.Value);
              symLine.Stroke.Color = clr;
              if (clr.A != 255)
                symLine.Stroke.Opacity = clr.A / 255.0F;
              break;
            case "line-opacity":
              symLine.Stroke.Opacity = Convert.ToSingle(pv.Value);
              break;
            case "line-join":
              symLine.Stroke.LineJoin = ToLineJoin(pv.Value);
              break;
            case "line-cap":
              symLine.Stroke.LineCap = ToLineCap(pv.Value);
              break;
            case "line-dasharray":
              symLine.Stroke.DashArray = ConvertUtility.ToFloatArray(pv.Value);
              break;
            case "line-miterlimit":
              symLine.Stroke.MiterLimit = Convert.ToSingle(pv.Value);
              break;
            case "line-dash-offset":
              symLine.Stroke.DashOffset = Convert.ToSingle(pv.Value);
              break;
            case "line-comp-op":
              UnsupportedProperty(pv);
              AddProperty(symLine, "comp-op", pv.Value);
              break;
            case "line-rasterizer":
              UnsupportedProperty(pv);
              break;
            case "line-simplify":
              geomTrans.Simplify = pv.Value;
              break;
            case "line-simplify-algorithm":
              geomTrans.SimplifyAlgorithm = pv.Value;
              break;
            case "line-smooth":
              geomTrans.Smooth = pv.Value;
              break;
            case "line-offset":
              geomTrans.Offset = pv.Value;
              break;
            case "line-geometry-transform":
              geomTrans.GeometryTransform = pv.Value;
              break;
          }
        }
      }
      catch(Exception ex)
      {
        ThrowParsingException(ex, pv);
      }

      symLine.GeometryExpression = ToGeometryExpression(geomTrans);

      return symLine;
    }
    private PolygonSymbolizer CreatePolygonSymbolizer(NodePropertyValue[] properties)
    {
      PolygonSymbolizer symPolygon = new PolygonSymbolizer();
      symPolygon.Fill.Color = Color.Gray;
      symPolygon.Fill.Outlined = false;
      symPolygon.Clip = true;

      GraphicFill gFill = properties[0].Name.StartsWith("polygon-pattern-") ? new GraphicFill() : null;
      if (gFill != null)
      {
        symPolygon.Fill.GraphicFill = gFill;
        symPolygon.Fill.Opacity = 0.0F;
      }

      GeometryTransformInfo geomTrans = new GeometryTransformInfo();

      NodePropertyValue pv = null;

      try
      {
        int nProps = properties.Length;

        for (int i = 0; i < nProps; i++)
        {
          pv = properties[i];

          if (gFill != null)
          {
            switch (pv.Name)
            {
              case "polygon-pattern-file":
                ExternalGraphicSymbol egs = new ExternalGraphicSymbol();
                egs.Path = ToPath(pv.Value);
                gFill.GraphicSymbols.Add(egs);
                break;
              case "polygon-pattern-opacity":
                gFill.Opacity = Convert.ToSingle(pv.Value);
                break;
              case "polygon-pattern-comp-op":
                AddProperty(symPolygon, "comp-op", pv.Value);
                break;
            }
          }
          else
          {
            switch (pv.Name)
            {
              case "polygon-fill":
                Color clr = ColorUtility.FromHtml(pv.Value);
                symPolygon.Fill.Color = clr;
                if (clr.A != 255)
                  symPolygon.Fill.Opacity = clr.A / 255.0F;
                break;
              case "polygon-opacity":
                symPolygon.Fill.Opacity = Convert.ToSingle(pv.Value);
                break;
              case "polygon-gamma":
                UnsupportedProperty(pv);
                break;
              case "polygon-gamma-method":
                UnsupportedProperty(pv);
                break;
              case "polygon-clip":
                symPolygon.Clip = Convert.ToBoolean(pv.Value);
                break;
              case "polygon-comp-op":
                UnsupportedProperty(pv);
                AddProperty(symPolygon, "comp-op", pv.Value);
                break;
              case "polygon-simplify":
                geomTrans.Simplify = pv.Value;
                break;
              case "polygon-simplify-algorithm":
                geomTrans.SimplifyAlgorithm = pv.Value;
                break;
              case "polygon-smooth":
                geomTrans.Smooth = pv.Value;
                break;
              case "polygon-offset":
                geomTrans.Offset = pv.Value;
                break;
              case "polygon-geometry-transform":
                geomTrans.GeometryTransform = pv.Value;
                break;
            }
          }
        }
      }
      catch (Exception ex)
      {
        ThrowParsingException(ex, pv);
      }

      symPolygon.GeometryExpression = ToGeometryExpression(geomTrans);

      return symPolygon;
    }
    private TextSymbolizer CreateTextSymbolizer(NodePropertyValue[] properties)
    {
      TextSymbolizer symText = new TextSymbolizer();
      symText.Clip = true;
      symText.TextLayout.Alignment = TextAlignment.CenterAligned;

      GeometryTransformInfo geomTrans = new GeometryTransformInfo();
      LabelPlacementInfo lpi = new LabelPlacementInfo();
      lpi.Symbolizer = (int)SymbolizerType.Text;
      TextLayoutBlock textBlock = new TextLayoutBlock();
      string textTransform = null;
      int blockIndex = -1;
      int nProps = properties.Length;

      NodePropertyValue pv = null;

      try
      {
        for (int i = 0; i < nProps; i++)
        {
          pv = properties[i];

          switch (pv.Name)
          {
            case "text-name":
              if (pv.Value != null)
              {
                string textExpr = ToExpression(pv.Value);
                if (!string.IsNullOrEmpty(textTransform))
                  textExpr = GetTextTransform(textExpr, textTransform);
                textBlock.TextExpression = textExpr;

                // TODO
                if (textBlock.TextExpression == string.Empty)
                  symText.Enabled = false;

                blockIndex++;
                if (blockIndex > 0)
                  textBlock = new TextLayoutBlock();

                symText.TextLayout.Blocks.Add(textBlock);
              }
              break;
            case "text-face-name":
              SetFontName(textBlock, pv.Value);
              break;
            case "text-transform":
              if (string.IsNullOrEmpty(textBlock.TextExpression))
                textTransform = pv.Value;
              else
                textBlock.TextExpression = GetTextTransform(textBlock.TextExpression, pv.Value);
              break;
            case "text-fill":
              Color clr = ColorUtility.FromHtml(pv.Value);
              textBlock.TextFormat.TextStyle.Color = clr;
              if (clr.A != 255)
                textBlock.TextFormat.TextStyle.Opacity = clr.A / 255.0F;
              break;
            case "text-opacity":
              textBlock.TextFormat.TextStyle.Opacity = Convert.ToSingle(pv.Value);
              break;
            case "text-size":
              textBlock.TextFormat.TextStyle.Font.Size = Convert.ToSingle(pv.Value);
              break;
            case "text-halo-radius":
              textBlock.TextFormat.TextStyle.Halo.Radius = Convert.ToSingle(pv.Value);
              break;
            case "text-halo-fill":
              Color clr2 = ColorUtility.FromHtml(pv.Value);
              textBlock.TextFormat.TextStyle.Halo.Color = clr2;
              if (clr2.A != 255)
                textBlock.TextFormat.TextStyle.Halo.Opacity = clr2.A / 255F;
              break;
            case "text-halo-opacity": // This property does not exist in the specification.
              textBlock.TextFormat.TextStyle.Halo.Opacity = Convert.ToSingle(pv.Value);
              break;
            case "text-halo-rasterizer":
              // TODO
              break;
            case "text-ratio":
              //TODO
              break;
            case "text-clip":
              symText.Clip = Convert.ToBoolean(pv.Value);
              break;
            case "text-align":
              textBlock.TextFormat.TextAlignment = ToTextAlignment(pv.Value);
              break;
            case "text-horizontal-alignment":
              // TODO
              break;
            case "text-vertical-alignment":
              // TODO
              break;
            case "text-wrap-width":
              textBlock.TextFormat.TextWrapping.MaxWidth = Convert.ToUInt32(pv.Value);
              textBlock.TextFormat.TextWrapping.Mode = Drawing.Text.TextWrapMode.WrapByMaxWidthPixels;
              break;
            case "text-wrap-before":
              TextWrapping tw = textBlock.TextFormat.TextWrapping;
              if (tw.Characters == null || tw.Characters.Length == 0)
                tw.Characters = new WrapCharacter[] { new WrapCharacter() };
              tw.Characters[0].WrapType = CharacterWrapType.Before;
              break;
            case "text-wrap-character":
              TextWrapping tw2 = textBlock.TextFormat.TextWrapping;
              if (tw2.Characters == null || tw2.Characters.Length == 0)
                tw2.Characters = new WrapCharacter[] { new WrapCharacter() };
              tw2.Characters[0].Character = pv.Value;
              break;
            case "text-character-spacing":
              textBlock.TextFormat.TextSpacing.CharacterSpacing = Convert.ToSingle(pv.Value);
              break;
            case "text-line-spacing":
              textBlock.TextFormat.TextSpacing.Leading = Convert.ToSingle(pv.Value);
              break;
            case "text-allow-overlap":
              symText.LabelBehaviour.AllowOverlap = Convert.ToBoolean(pv.Value);
              break;
            case "text-min-distance":
              symText.LabelBehaviour.CollisionMeasures.Add(string.Format("MinimumDistance({0})", pv.Value));
              break;
            case "text-avoid-edges":
              symText.LabelBehaviour.AvoidEdges = Convert.ToBoolean(pv.Value);
              break;
            case "text-spacing":
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "text-max-char-angle-delta":
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "text-label-position-tolerance":
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "text-min-padding":
              UnsupportedProperty(pv);
              break;
            case "text-min-path-length":
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "text-orientation":
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "text-placement":
              lpi.Placement = pv.Value;
              if (lpi.Placement == "line")
                geomTrans.OffsetCurve = true;
              break;
            case "text-placement-type":
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "text-placements":
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "text-dx":
              geomTrans.DisplacementX = pv.Value;
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "text-dy":
              geomTrans.DisplacementY = pv.Value;
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "text-comp-op":
              UnsupportedProperty(pv);
              break;
            default:
              break;
          }
        }
      }
      catch (Exception ex)
      {
        ThrowParsingException(ex, pv);
      }

      ApplyTextBlockFormat(symText.TextLayout);
      symText.LabelPlacement = CreateLabelPlacement(lpi);
      if ("point".Equals(lpi.Placement))
        geomTrans.DisplacementX = geomTrans.DisplacementY = string.Empty;
      symText.GeometryExpression = ToGeometryExpression(geomTrans);

      return symText;
    }
    private LinePatternSymbolizer CreateLinePatternSymbolizer(NodePropertyValue[] properties)
    {
      LinePatternSymbolizer symLinePattern = new LinePatternSymbolizer();
      symLinePattern.Clip = true;
      symLinePattern.LabelBehaviour.AllowOverlap = true;
      symLinePattern.LabelBehaviour.CollisionDetectable = false;

      GeometryTransformInfo geomTrans = new GeometryTransformInfo();
      int nProps = properties.Length;

      NodePropertyValue pv = null;

      try
      {
        for (int i = 0; i < nProps; i++)
        {
          pv = properties[i];

          switch (pv.Name)
          {
            case "line-pattern-file":
              symLinePattern.FileName = ToPath(pv.Value);
              break;
            case "line-pattern-clip":
              symLinePattern.Clip = Convert.ToBoolean(pv.Value);
              break;
            case "line-pattern-simplify":
              geomTrans.Simplify = pv.Value;
              break;
            case "line-pattern-simplify-algorithm":
              geomTrans.SimplifyAlgorithm = pv.Value;
              break;
            case "line-pattern-smooth":
              geomTrans.Smooth = pv.Value;
              break;
            case "line-pattern-offset":
              geomTrans.Offset = pv.Value;
              break;
            case "line-pattern-geometry-transform":
              geomTrans.GeometryTransform = pv.Value;
              break;
            case "line-pattern-comp-op":
             // UnsupportedProperty(pv.Name);
              AddProperty(symLinePattern, "comp-op", pv.Value);
              break;
          }
        }
      }
      catch (Exception ex)
      {
        ThrowParsingException(ex, pv);
      }

      symLinePattern.GeometryExpression = ToGeometryExpression(geomTrans);

      return symLinePattern;
    }
    private GraphicTextSymbolizer CreateGraphicTextSymbolizer(NodePropertyValue[] properties)
    {
      GraphicTextSymbolizer symText = new GraphicTextSymbolizer();
      symText.Clip = true;
      symText.TextLayout.Alignment = TextAlignment.CenterAligned;
      ExternalGraphicSymbol gsImage = new ExternalGraphicSymbol();
      symText.Graphic.GraphicSymbols.Add(gsImage);

      GeometryTransformInfo geomTrans = new GeometryTransformInfo();

      LabelPlacementInfo lpi = new LabelPlacementInfo();
      lpi.Placement = "point";
      lpi.Symbolizer = (int)SymbolizerType.Shield;

      int blockIndex = -1;
      TextLayoutBlock textBlock = new TextLayoutBlock();
      string textTransform = null;
      int nProps = properties.Length;
      float text_dx = 0F, text_dy = 0F;

      NodePropertyValue pv = null;

      try
      {
        for (int i = 0; i < nProps; i++)
        {
          pv = properties[i];

          switch (pv.Name)
          {
            case "shield-name":
              if (pv.Value != null)
              {
                string textExpr = ToExpression(pv.Value);
                if (!string.IsNullOrEmpty(textTransform))
                  textExpr = GetTextTransform(textExpr, textTransform);
                textBlock.TextExpression = textExpr;

                blockIndex++;
                if (blockIndex > 0)
                  textBlock = new TextLayoutBlock();

                symText.TextLayout.Blocks.Add(textBlock);
              }
              break;
            case "shield-face-name":
              SetFontName(textBlock, pv.Value);
              break;
            case "shield-file":
              gsImage.Path = ToPath(pv.Value);
              break;
            case "shield-text-transform":
              if (string.IsNullOrEmpty(textBlock.TextExpression))
                textTransform = pv.Value;
              else
                textBlock.TextExpression = GetTextTransform(textBlock.TextExpression, pv.Value);
              break;
            case "shield-fill":
              textBlock.TextFormat.TextStyle.Color = ColorUtility.FromHtml(pv.Value);
              break;
            case "shield-text-opacity":
              textBlock.TextFormat.TextStyle.Opacity = Convert.ToSingle(pv.Value);
              break;
            case "shield-opacity":
              gsImage.Opacity = Convert.ToSingle(pv.Value);
              break;
            case "shield-size":
              textBlock.TextFormat.TextStyle.Font.Size = Convert.ToSingle(pv.Value);
              break;
            case "shield-halo-radius":
              textBlock.TextFormat.TextStyle.Halo.Radius = Convert.ToSingle(pv.Value);
              break;
            case "shield-halo-fill":
              Color clr = ColorUtility.FromHtml(pv.Value);
              textBlock.TextFormat.TextStyle.Halo.Color = clr;
              if (clr.A != 255)
                textBlock.TextFormat.TextStyle.Halo.Opacity = clr.A / 255.0F;
              break;
            case "shield-halo-opacity":
              textBlock.TextFormat.TextStyle.Halo.Opacity = Convert.ToSingle(pv.Value);
              break;
            case "shield-clip":
              symText.Clip = Convert.ToBoolean(pv.Value);
              break;
            case "shield-horizontal-alignment":
              // TODO
              break;
            case "shield-vertical-alignment":
              // TODO
              break;
            case "shield-justify-alignment":
              // TODO
              break;
            case "shield-transform":
              //TODO
              break;
            case "shield-wrap-width":
              textBlock.TextFormat.TextWrapping.MaxWidth = Convert.ToUInt32(pv.Value);
              textBlock.TextFormat.TextWrapping.Mode = Drawing.Text.TextWrapMode.WrapByMaxWidthPixels;
              break;
            case "shield-wrap-before":
              TextWrapping tw = textBlock.TextFormat.TextWrapping;
              if (tw.Characters == null)
                tw.Characters = new WrapCharacter[] { new WrapCharacter() };

              tw.Characters[0].WrapType = CharacterWrapType.Before;
              break;
            case "shield-wrap-character":
              TextWrapping tw2 = textBlock.TextFormat.TextWrapping;
              if (tw2.Characters == null)
                tw2.Characters = new WrapCharacter[] { new WrapCharacter() };

              tw2.Characters[0].Character = pv.Value;
              break;
            case "shield-character-spacing":
              textBlock.TextFormat.TextSpacing.CharacterSpacing = Convert.ToSingle(pv.Value);
              break;
            case "shield-line-spacing":
              textBlock.TextFormat.TextSpacing.Leading = Convert.ToSingle(pv.Value);
              break;
            case "shield-allow-overlap":
              symText.LabelBehaviour.AllowOverlap = Convert.ToBoolean(pv.Value);
              break;
            case "shield-min-distance":
              symText.LabelBehaviour.CollisionMeasures.Add(string.Format("MinimumDistance({0})", pv.Value));
              break;
            case "shield-avoid-edges":
              symText.LabelBehaviour.AvoidEdges = Convert.ToBoolean(pv.Value);
              break;
            case "shield-spacing":
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "shield-min-padding":
              UnsupportedProperty(pv);
              break;
            case "shield-placement-type":
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "shield-placements":
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "shield-text-dx":
              text_dx = Convert.ToSingle(pv.Value);
              break;
            case "shield-text-dy":
              text_dy = Convert.ToSingle(pv.Value);
              break;
            case "shield-dx":
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "shield-dy":
              lpi.Properties.Add(new KeyValuePair<string, string>(pv.Name, pv.Value));
              break;
            case "shield-comp-op":
              UnsupportedProperty(pv);
              break;
            default:
              break;
          }
        }
      }
      catch (Exception ex)
      {
        ThrowParsingException(ex, pv);
      }

      if (text_dx != 0F || text_dy != 0F)
        symText.Graphic.Size = new SizeF(text_dx, text_dy);

      ApplyTextBlockFormat(symText.TextLayout);
      symText.LabelPlacement = CreateLabelPlacement(lpi);
      if ("point".Equals(lpi.Placement))
        geomTrans.DisplacementX = geomTrans.DisplacementY = string.Empty;
      symText.GeometryExpression = ToGeometryExpression(geomTrans);

      return symText;
    }
    private string ToGeometryExpression(GeometryTransformInfo gt)
    {
      if (!gt.IsEmpty)
      {
        string expr = "GeometryTransformations.ViewTransformation([_geom_], [_ViewTransformation_])";

        if (!string.IsNullOrEmpty(gt.DisplacementX) || !string.IsNullOrEmpty(gt.DisplacementY))
        {
          string dx = string.IsNullOrEmpty(gt.DisplacementX) ? "0" : gt.DisplacementX;
          string dy = string.IsNullOrEmpty(gt.DisplacementY) ? "0" : gt.DisplacementY;

          if (gt.OffsetCurve)
            expr = string.Format("GeometryTransformations.OffsetCurve({0}, {1}, 0)", expr, dy);
          else
            expr = string.Format("GeometryTransformations.Offset({0}, {1}, {2})", expr, dx, dy);
        }

        if (!string.IsNullOrEmpty(gt.Simplify))
          expr = string.Format("GeometryTransformations.Simplify({0}, {1})", expr, gt.Simplify);
        if (!string.IsNullOrEmpty(gt.Offset))
          expr = string.Format("GeometryTransformations.OffsetCurve({0}, {1}, 0)", expr, gt.Offset);
        if (!string.IsNullOrEmpty(gt.Smooth))
          expr = string.Format("GeometryTransformations.Smooth({0}, {1})", expr, gt.Smooth);

        return expr;
      }
       
      return null;
    }