第07章-样式与主题渲染

第07章:样式与主题渲染

7.1 样式系统概述

7.1.1 样式类层次结构

Style (抽象基类)
    │
    ├── VectorStyle      矢量样式(点、线、面)
    │
    └── LabelStyle       标注样式

7.1.2 样式基类

namespace SharpMap.Styles
{
    public abstract class Style : IStyle
    {
        // 启用/禁用
        public bool Enabled { get; set; }
        
        // 最小可见比例尺
        public double MinVisible { get; set; }
        
        // 最大可见比例尺
        public double MaxVisible { get; set; }
    }
}

7.2 VectorStyle 矢量样式

7.2.1 基本属性

using SharpMap.Styles;
using System.Drawing;
using System.Drawing.Drawing2D;

var style = new VectorStyle();

// 填充设置(多边形内部)
style.Fill = new SolidBrush(Color.LightGreen);

// 边框设置(多边形边界)
style.Outline = new Pen(Color.DarkGreen, 2);
style.EnableOutline = true;

// 线设置(线要素)
style.Line = new Pen(Color.Blue, 3);

// 点设置
style.PointColor = Brushes.Red;
style.PointSize = 10;

// 符号设置
style.Symbol = null;  // 使用点符号图片
style.SymbolOffset = new PointF(0, 0);
style.SymbolRotation = 0;
style.SymbolScale = 1.0f;

7.2.2 多边形样式

// 纯色填充
var solidStyle = new VectorStyle
{
    Fill = new SolidBrush(Color.FromArgb(150, 100, 200, 100)),
    Outline = new Pen(Color.DarkGreen, 2),
    EnableOutline = true
};

// 渐变填充
var gradientStyle = new VectorStyle
{
    Fill = new LinearGradientBrush(
        new Point(0, 0), 
        new Point(100, 100),
        Color.LightBlue, 
        Color.DarkBlue),
    EnableOutline = true,
    Outline = new Pen(Color.Navy, 1)
};

// 图案填充
var hatchStyle = new VectorStyle
{
    Fill = new HatchBrush(HatchStyle.DiagonalCross, Color.Black, Color.White),
    EnableOutline = true,
    Outline = new Pen(Color.Black, 1)
};

// 透明填充(仅边框)
var outlineOnlyStyle = new VectorStyle
{
    Fill = null,
    EnableOutline = true,
    Outline = new Pen(Color.Red, 2)
};

7.2.3 线样式

// 实线
var solidLineStyle = new VectorStyle
{
    Line = new Pen(Color.Blue, 3)
    {
        LineJoin = LineJoin.Round,
        StartCap = LineCap.Round,
        EndCap = LineCap.Round
    }
};

// 虚线
var dashedLineStyle = new VectorStyle
{
    Line = new Pen(Color.Red, 2)
    {
        DashStyle = DashStyle.Dash
    }
};

// 点线
var dottedLineStyle = new VectorStyle
{
    Line = new Pen(Color.Green, 2)
    {
        DashStyle = DashStyle.Dot
    }
};

// 自定义虚线模式
var customDashStyle = new VectorStyle
{
    Line = new Pen(Color.Orange, 2)
    {
        DashStyle = DashStyle.Custom,
        DashPattern = new float[] { 5, 2, 1, 2 }  // 长线-短空-短线-短空
    }
};

// 带箭头的线
var arrowLineStyle = new VectorStyle
{
    Line = new Pen(Color.Blue, 2)
    {
        EndCap = LineCap.ArrowAnchor
    }
};

// 双线(道路效果)
public class DoubleLineLayer : VectorLayer
{
    public override void Render(Graphics g, Map map)
    {
        // 先渲染粗线(背景)
        var backgroundStyle = new VectorStyle
        {
            Line = new Pen(Color.Black, 6)
        };
        Style = backgroundStyle;
        base.Render(g, map);
        
        // 再渲染细线(前景)
        var foregroundStyle = new VectorStyle
        {
            Line = new Pen(Color.Yellow, 4)
        };
        Style = foregroundStyle;
        base.Render(g, map);
    }
}

7.2.4 点样式

// 简单圆点
var circleStyle = new VectorStyle
{
    PointColor = Brushes.Red,
    PointSize = 10
};

// 使用图片符号
var imageStyle = new VectorStyle
{
    Symbol = Image.FromFile("marker.png"),
    SymbolScale = 1.0f,
    SymbolOffset = new PointF(0, -16)  // 图标底部对齐点位置
};

// 自定义符号渲染
public class CustomSymbolRenderer
{
    public static Bitmap CreateStarSymbol(int size, Color fillColor, Color outlineColor)
    {
        var bitmap = new Bitmap(size, size);
        using (var g = Graphics.FromImage(bitmap))
        {
            g.SmoothingMode = SmoothingMode.AntiAlias;
            
            // 计算五角星顶点
            var points = new PointF[10];
            float outerRadius = size / 2f - 2;
            float innerRadius = outerRadius * 0.4f;
            float centerX = size / 2f;
            float centerY = size / 2f;
            
            for (int i = 0; i < 10; i++)
            {
                float radius = i % 2 == 0 ? outerRadius : innerRadius;
                float angle = (float)(i * Math.PI / 5 - Math.PI / 2);
                points[i] = new PointF(
                    centerX + radius * (float)Math.Cos(angle),
                    centerY + radius * (float)Math.Sin(angle)
                );
            }
            
            using (var brush = new SolidBrush(fillColor))
            using (var pen = new Pen(outlineColor, 1))
            {
                g.FillPolygon(brush, points);
                g.DrawPolygon(pen, points);
            }
        }
        return bitmap;
    }
}

// 使用自定义符号
var starSymbol = CustomSymbolRenderer.CreateStarSymbol(24, Color.Gold, Color.DarkOrange);
var starStyle = new VectorStyle
{
    Symbol = starSymbol,
    SymbolOffset = new PointF(0, 0)
};

7.3 LabelStyle 标注样式

7.3.1 基本属性

using SharpMap.Styles;

var labelStyle = new LabelStyle();

// 字体
labelStyle.Font = new Font("Microsoft YaHei", 12, FontStyle.Bold);

// 颜色
labelStyle.ForeColor = Color.Black;
labelStyle.BackColor = new SolidBrush(Color.FromArgb(200, 255, 255, 200));

// 光晕
labelStyle.Halo = new Pen(Color.White, 3);

// 对齐
labelStyle.HorizontalAlignment = LabelStyle.HorizontalAlignmentEnum.Center;
labelStyle.VerticalAlignment = LabelStyle.VerticalAlignmentEnum.Middle;

// 偏移
labelStyle.Offset = new PointF(0, -15);

// 旋转
labelStyle.Rotation = 0;

// 碰撞检测
labelStyle.CollisionDetection = true;
labelStyle.CollisionBuffer = new SizeF(10, 10);

7.3.2 高级标注样式

// 带背景框的标注
var boxLabelStyle = new LabelStyle
{
    Font = new Font("Arial", 10, FontStyle.Bold),
    ForeColor = Color.White,
    BackColor = new SolidBrush(Color.FromArgb(200, 0, 100, 200)),
    HorizontalAlignment = LabelStyle.HorizontalAlignmentEnum.Center,
    VerticalAlignment = LabelStyle.VerticalAlignmentEnum.Middle
};

// 带光晕效果的标注
var haloLabelStyle = new LabelStyle
{
    Font = new Font("Arial", 12),
    ForeColor = Color.Black,
    BackColor = null,
    Halo = new Pen(Color.White, 3),
    HorizontalAlignment = LabelStyle.HorizontalAlignmentEnum.Center,
    VerticalAlignment = LabelStyle.VerticalAlignmentEnum.Bottom,
    Offset = new PointF(0, -5)
};

// 道路标注(沿线显示)
var roadLabelStyle = new LabelStyle
{
    Font = new Font("Arial", 9),
    ForeColor = Color.FromArgb(100, 100, 100),
    BackColor = null,
    Halo = new Pen(Color.FromArgb(200, 255, 255, 255), 2),
    HorizontalAlignment = LabelStyle.HorizontalAlignmentEnum.Center,
    VerticalAlignment = LabelStyle.VerticalAlignmentEnum.Middle,
    IgnoreLength = false,  // 线太短时不显示标注
    CollisionDetection = true
};

7.4 主题渲染

7.4.1 ITheme 接口

namespace SharpMap.Rendering.Thematics
{
    public interface ITheme
    {
        IStyle GetStyle(FeatureDataRow row);
    }
}

7.4.2 唯一值主题

using SharpMap.Rendering.Thematics;

// 创建唯一值主题
var uniqueTheme = new UniqueValuesTheme<string>("LAND_TYPE");

// 添加各值对应的样式
uniqueTheme.AddStyle("Forest", new VectorStyle
{
    Fill = new SolidBrush(Color.DarkGreen),
    Outline = new Pen(Color.Black, 1),
    EnableOutline = true
});

uniqueTheme.AddStyle("Water", new VectorStyle
{
    Fill = new SolidBrush(Color.Blue),
    Outline = new Pen(Color.DarkBlue, 1),
    EnableOutline = true
});

uniqueTheme.AddStyle("Urban", new VectorStyle
{
    Fill = new SolidBrush(Color.Gray),
    Outline = new Pen(Color.Black, 1),
    EnableOutline = true
});

// 设置默认样式(其他值使用)
uniqueTheme.DefaultStyle = new VectorStyle
{
    Fill = new SolidBrush(Color.LightGray),
    Outline = new Pen(Color.Gray, 1),
    EnableOutline = true
};

// 应用到图层
vectorLayer.Theme = uniqueTheme;

7.4.3 分级主题

// 创建分级主题
var gradientTheme = new GradientTheme("POPULATION", 0, 1000000,
    new VectorStyle
    {
        Fill = new SolidBrush(Color.FromArgb(255, 255, 200)),
        Outline = new Pen(Color.Black, 1),
        EnableOutline = true
    },
    new VectorStyle
    {
        Fill = new SolidBrush(Color.FromArgb(255, 0, 0)),
        Outline = new Pen(Color.Black, 1),
        EnableOutline = true
    });

vectorLayer.Theme = gradientTheme;

7.4.4 自定义主题

public class CustomTheme : ITheme
{
    private readonly string _attributeName;
    private readonly Dictionary<string, VectorStyle> _styles;
    private readonly VectorStyle _defaultStyle;
    
    public CustomTheme(string attributeName)
    {
        _attributeName = attributeName;
        _styles = new Dictionary<string, VectorStyle>();
        _defaultStyle = new VectorStyle
        {
            Fill = new SolidBrush(Color.LightGray),
            Outline = new Pen(Color.Gray, 1),
            EnableOutline = true
        };
    }
    
    public void AddStyle(string value, VectorStyle style)
    {
        _styles[value] = style;
    }
    
    public IStyle GetStyle(FeatureDataRow row)
    {
        var value = row[_attributeName]?.ToString();
        
        if (!string.IsNullOrEmpty(value) && _styles.ContainsKey(value))
        {
            return _styles[value];
        }
        
        return _defaultStyle;
    }
}

// 使用自定义主题
var customTheme = new CustomTheme("CATEGORY");
customTheme.AddStyle("A", new VectorStyle { Fill = new SolidBrush(Color.Red) });
customTheme.AddStyle("B", new VectorStyle { Fill = new SolidBrush(Color.Blue) });
customTheme.AddStyle("C", new VectorStyle { Fill = new SolidBrush(Color.Green) });

vectorLayer.Theme = customTheme;

7.4.5 数值分级主题

public class ClassBreaksTheme : ITheme
{
    private readonly string _attributeName;
    private readonly List<(double minValue, double maxValue, VectorStyle style)> _breaks;
    private readonly VectorStyle _defaultStyle;
    
    public ClassBreaksTheme(string attributeName)
    {
        _attributeName = attributeName;
        _breaks = new List<(double, double, VectorStyle)>();
        _defaultStyle = new VectorStyle
        {
            Fill = new SolidBrush(Color.White),
            EnableOutline = true,
            Outline = new Pen(Color.Gray, 1)
        };
    }
    
    public void AddBreak(double minValue, double maxValue, VectorStyle style)
    {
        _breaks.Add((minValue, maxValue, style));
    }
    
    public IStyle GetStyle(FeatureDataRow row)
    {
        if (row[_attributeName] == DBNull.Value)
            return _defaultStyle;
            
        double value = Convert.ToDouble(row[_attributeName]);
        
        foreach (var breakItem in _breaks)
        {
            if (value >= breakItem.minValue && value < breakItem.maxValue)
            {
                return breakItem.style;
            }
        }
        
        return _defaultStyle;
    }
}

// 使用数值分级主题
var populationTheme = new ClassBreaksTheme("POPULATION");

populationTheme.AddBreak(0, 100000, new VectorStyle
{
    Fill = new SolidBrush(Color.FromArgb(255, 255, 178)),
    Outline = new Pen(Color.Black, 1),
    EnableOutline = true
});

populationTheme.AddBreak(100000, 500000, new VectorStyle
{
    Fill = new SolidBrush(Color.FromArgb(254, 204, 92)),
    Outline = new Pen(Color.Black, 1),
    EnableOutline = true
});

populationTheme.AddBreak(500000, 1000000, new VectorStyle
{
    Fill = new SolidBrush(Color.FromArgb(253, 141, 60)),
    Outline = new Pen(Color.Black, 1),
    EnableOutline = true
});

populationTheme.AddBreak(1000000, double.MaxValue, new VectorStyle
{
    Fill = new SolidBrush(Color.FromArgb(227, 26, 28)),
    Outline = new Pen(Color.Black, 1),
    EnableOutline = true
});

vectorLayer.Theme = populationTheme;

7.4.6 条件主题

public class ConditionalTheme : ITheme
{
    private readonly List<(Func<FeatureDataRow, bool> condition, VectorStyle style)> _conditions;
    private readonly VectorStyle _defaultStyle;
    
    public ConditionalTheme(VectorStyle defaultStyle = null)
    {
        _conditions = new List<(Func<FeatureDataRow, bool>, VectorStyle)>();
        _defaultStyle = defaultStyle ?? new VectorStyle
        {
            Fill = new SolidBrush(Color.LightGray),
            EnableOutline = true
        };
    }
    
    public void AddCondition(Func<FeatureDataRow, bool> condition, VectorStyle style)
    {
        _conditions.Add((condition, style));
    }
    
    public IStyle GetStyle(FeatureDataRow row)
    {
        foreach (var item in _conditions)
        {
            if (item.condition(row))
            {
                return item.style;
            }
        }
        return _defaultStyle;
    }
}

// 使用条件主题
var conditionalTheme = new ConditionalTheme();

// 首都城市 - 红色五角星
conditionalTheme.AddCondition(
    row => row["IS_CAPITAL"]?.ToString() == "Y",
    new VectorStyle
    {
        Symbol = CreateStarSymbol(24, Color.Red),
        PointSize = 24
    });

// 大城市 - 大蓝点
conditionalTheme.AddCondition(
    row => Convert.ToDouble(row["POPULATION"] ?? 0) > 1000000,
    new VectorStyle
    {
        PointColor = Brushes.Blue,
        PointSize = 12
    });

// 普通城市 - 小黑点
conditionalTheme.AddCondition(
    row => true,  // 默认条件
    new VectorStyle
    {
        PointColor = Brushes.Black,
        PointSize = 6
    });

citiesLayer.Theme = conditionalTheme;

7.5 动态样式

7.5.1 基于比例尺的样式

public class ScaleBasedTheme : ITheme
{
    private readonly Map _map;
    private readonly Dictionary<double, VectorStyle> _scaleStyles;
    
    public ScaleBasedTheme(Map map)
    {
        _map = map;
        _scaleStyles = new Dictionary<double, VectorStyle>();
    }
    
    public void AddScaleStyle(double minScale, VectorStyle style)
    {
        _scaleStyles[minScale] = style;
    }
    
    public IStyle GetStyle(FeatureDataRow row)
    {
        double currentScale = _map.MapScale;
        
        VectorStyle selectedStyle = null;
        double selectedScale = 0;
        
        foreach (var kvp in _scaleStyles)
        {
            if (currentScale >= kvp.Key && kvp.Key >= selectedScale)
            {
                selectedScale = kvp.Key;
                selectedStyle = kvp.Value;
            }
        }
        
        return selectedStyle ?? new VectorStyle();
    }
}

// 使用
var scaleTheme = new ScaleBasedTheme(map);

// 大比例尺(详细)
scaleTheme.AddScaleStyle(0, new VectorStyle
{
    Fill = new SolidBrush(Color.LightGreen),
    Outline = new Pen(Color.DarkGreen, 2),
    EnableOutline = true
});

// 中等比例尺
scaleTheme.AddScaleStyle(50000, new VectorStyle
{
    Fill = new SolidBrush(Color.Green),
    Outline = new Pen(Color.DarkGreen, 1),
    EnableOutline = true
});

// 小比例尺(概览)
scaleTheme.AddScaleStyle(500000, new VectorStyle
{
    Fill = new SolidBrush(Color.DarkGreen),
    EnableOutline = false
});

7.5.2 高亮样式

public class HighlightManager
{
    private readonly VectorLayer _layer;
    private readonly VectorStyle _normalStyle;
    private readonly VectorStyle _highlightStyle;
    private HashSet<uint> _highlightedIds;
    
    public HighlightManager(VectorLayer layer, VectorStyle normalStyle, VectorStyle highlightStyle)
    {
        _layer = layer;
        _normalStyle = normalStyle;
        _highlightStyle = highlightStyle;
        _highlightedIds = new HashSet<uint>();
        
        // 设置主题
        _layer.Theme = new HighlightTheme(this, normalStyle, highlightStyle);
    }
    
    public void Highlight(uint id)
    {
        _highlightedIds.Add(id);
    }
    
    public void ClearHighlight()
    {
        _highlightedIds.Clear();
    }
    
    public bool IsHighlighted(uint id) => _highlightedIds.Contains(id);
    
    private class HighlightTheme : ITheme
    {
        private readonly HighlightManager _manager;
        private readonly VectorStyle _normalStyle;
        private readonly VectorStyle _highlightStyle;
        
        public HighlightTheme(HighlightManager manager, VectorStyle normalStyle, VectorStyle highlightStyle)
        {
            _manager = manager;
            _normalStyle = normalStyle;
            _highlightStyle = highlightStyle;
        }
        
        public IStyle GetStyle(FeatureDataRow row)
        {
            if (row["OID"] != DBNull.Value)
            {
                uint id = Convert.ToUInt32(row["OID"]);
                if (_manager.IsHighlighted(id))
                {
                    return _highlightStyle;
                }
            }
            return _normalStyle;
        }
    }
}

7.6 标注主题

7.6.1 动态标注样式

public class LabelTheme : ITheme
{
    private readonly string _sizeColumn;
    private readonly Font _baseFont;
    
    public LabelTheme(string sizeColumn, Font baseFont)
    {
        _sizeColumn = sizeColumn;
        _baseFont = baseFont;
    }
    
    public IStyle GetStyle(FeatureDataRow row)
    {
        double size = Convert.ToDouble(row[_sizeColumn] ?? 10);
        
        // 根据数值调整字体大小
        float fontSize = Math.Max(8, Math.Min(24, (float)(size / 100000)));
        
        return new LabelStyle
        {
            Font = new Font(_baseFont.FontFamily, fontSize, _baseFont.Style),
            ForeColor = Color.Black,
            Halo = new Pen(Color.White, 2),
            HorizontalAlignment = LabelStyle.HorizontalAlignmentEnum.Center,
            VerticalAlignment = LabelStyle.VerticalAlignmentEnum.Middle,
            CollisionDetection = true
        };
    }
}

// 使用
labelLayer.Theme = new LabelTheme("POPULATION", new Font("Arial", 10, FontStyle.Bold));

7.7 本章小结

本章详细介绍了 SharpMap 的样式系统和主题渲染:

  1. VectorStyle:掌握了多边形、线、点的样式设置
  2. LabelStyle:学习了标注样式的各种配置
  3. 唯一值主题:了解了基于属性值的分类渲染
  4. 分级主题:掌握了数值分级渲染方法
  5. 自定义主题:学习了实现自定义主题的方法
  6. 动态样式:了解了基于比例尺和高亮的动态样式

7.8 参考资源


下一章预告:第08章将详细介绍标注与标签图层的高级配置,包括碰撞检测、沿线标注等功能。

posted @ 2026-01-08 14:09  我才是银古  阅读(9)  评论(0)    收藏  举报