第11章 - 样式与符号化

第11章 - 样式与符号化

11.1 SLD 样式概述

11.1.1 SLD 规范

SLD(Styled Layer Descriptor)是 OGC 制定的样式描述规范,用于定义地图要素的可视化样式。

┌─────────────────────────────────────────────────────────────────────┐
│                        SLD 结构层次                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│   StyledLayerDescriptor                                             │
│       │                                                             │
│       └── NamedLayer / UserLayer                                    │
│               │                                                     │
│               └── UserStyle                                         │
│                       │                                             │
│                       └── FeatureTypeStyle                          │
│                               │                                     │
│                               └── Rule                              │
│                                       ├── Filter (可选)             │
│                                       ├── MinScaleDenominator       │
│                                       ├── MaxScaleDenominator       │
│                                       └── Symbolizer                │
│                                               ├── PointSymbolizer   │
│                                               ├── LineSymbolizer    │
│                                               ├── PolygonSymbolizer │
│                                               ├── TextSymbolizer    │
│                                               └── RasterSymbolizer  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

11.1.2 样式工厂

import org.geotools.api.style.*;
import org.geotools.factory.CommonFactoryFinder;

public class StyleFactoryExample {
    
    // 获取样式工厂
    private static final StyleFactory sf = CommonFactoryFinder.getStyleFactory();
    private static final FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
    
    public static void main(String[] args) {
        System.out.println("StyleFactory: " + sf.getClass().getSimpleName());
        System.out.println("FilterFactory: " + ff.getClass().getSimpleName());
    }
}

11.2 创建点样式

11.2.1 简单点样式

public class PointStyleExample {
    
    private static final StyleFactory sf = CommonFactoryFinder.getStyleFactory();
    private static final FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
    
    // 创建简单圆点样式
    public static Style createSimplePointStyle() {
        // 创建标记
        Mark mark = sf.createMark();
        mark.setWellKnownName(ff.literal("circle"));
        mark.setFill(sf.createFill(ff.literal(Color.RED)));
        mark.setStroke(sf.createStroke(ff.literal(Color.BLACK), ff.literal(1)));
        
        // 创建图形
        Graphic graphic = sf.createDefaultGraphic();
        graphic.graphicalSymbols().clear();
        graphic.graphicalSymbols().add(mark);
        graphic.setSize(ff.literal(10));
        
        // 创建点符号化器
        PointSymbolizer pointSymbolizer = sf.createPointSymbolizer();
        pointSymbolizer.setGraphic(graphic);
        
        // 创建规则
        Rule rule = sf.createRule();
        rule.symbolizers().add(pointSymbolizer);
        
        // 创建要素类型样式
        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        fts.rules().add(rule);
        
        // 创建样式
        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        
        return style;
    }
    
    // 使用不同形状
    public static Style createShapedPointStyle(String shape, Color fillColor, int size) {
        // 可用形状: circle, square, triangle, star, cross, x
        Mark mark = sf.createMark();
        mark.setWellKnownName(ff.literal(shape));
        mark.setFill(sf.createFill(ff.literal(fillColor)));
        mark.setStroke(sf.createStroke(ff.literal(Color.BLACK), ff.literal(1)));
        
        Graphic graphic = sf.createDefaultGraphic();
        graphic.graphicalSymbols().clear();
        graphic.graphicalSymbols().add(mark);
        graphic.setSize(ff.literal(size));
        
        PointSymbolizer ps = sf.createPointSymbolizer();
        ps.setGraphic(graphic);
        
        return createStyle(ps);
    }
    
    // 使用图标
    public static Style createIconPointStyle(String iconPath) throws Exception {
        ExternalGraphic externalGraphic = sf.createExternalGraphic(
            new URL("file:" + iconPath), "image/png");
        
        Graphic graphic = sf.createDefaultGraphic();
        graphic.graphicalSymbols().clear();
        graphic.graphicalSymbols().add(externalGraphic);
        graphic.setSize(ff.literal(32));
        
        PointSymbolizer ps = sf.createPointSymbolizer();
        ps.setGraphic(graphic);
        
        return createStyle(ps);
    }
    
    private static Style createStyle(Symbolizer symbolizer) {
        Rule rule = sf.createRule();
        rule.symbolizers().add(symbolizer);
        
        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        fts.rules().add(rule);
        
        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        
        return style;
    }
}

11.3 创建线样式

11.3.1 线符号化器

public class LineStyleExample {
    
    private static final StyleFactory sf = CommonFactoryFinder.getStyleFactory();
    private static final FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
    
    // 简单线样式
    public static Style createSimpleLineStyle(Color color, double width) {
        Stroke stroke = sf.createStroke(
            ff.literal(color),
            ff.literal(width)
        );
        
        LineSymbolizer lineSymbolizer = sf.createLineSymbolizer();
        lineSymbolizer.setStroke(stroke);
        
        return createStyle(lineSymbolizer);
    }
    
    // 虚线样式
    public static Style createDashedLineStyle(Color color, double width, 
            float[] dashArray) {
        
        Stroke stroke = sf.createStroke(
            ff.literal(color),
            ff.literal(width),
            ff.literal(1.0f),  // 不透明度
            ff.literal("round"),  // 线帽
            ff.literal("round"),  // 连接
            dashArray,
            ff.literal(0.0f),  // 虚线偏移
            null,
            null
        );
        
        LineSymbolizer lineSymbolizer = sf.createLineSymbolizer();
        lineSymbolizer.setStroke(stroke);
        
        return createStyle(lineSymbolizer);
    }
    
    // 道路样式(双层)
    public static Style createRoadStyle(Color casing, Color fill, 
            double casingWidth, double fillWidth) {
        
        // 外层(边框)
        Stroke casingStroke = sf.createStroke(
            ff.literal(casing),
            ff.literal(casingWidth)
        );
        LineSymbolizer casingLine = sf.createLineSymbolizer();
        casingLine.setStroke(casingStroke);
        
        // 内层(填充)
        Stroke fillStroke = sf.createStroke(
            ff.literal(fill),
            ff.literal(fillWidth)
        );
        LineSymbolizer fillLine = sf.createLineSymbolizer();
        fillLine.setStroke(fillStroke);
        
        Rule rule = sf.createRule();
        rule.symbolizers().add(casingLine);  // 先绘制边框
        rule.symbolizers().add(fillLine);    // 后绘制填充
        
        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        fts.rules().add(rule);
        
        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        
        return style;
    }
    
    private static Style createStyle(Symbolizer symbolizer) {
        Rule rule = sf.createRule();
        rule.symbolizers().add(symbolizer);
        
        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        fts.rules().add(rule);
        
        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        
        return style;
    }
}

11.4 创建面样式

11.4.1 多边形符号化器

public class PolygonStyleExample {
    
    private static final StyleFactory sf = CommonFactoryFinder.getStyleFactory();
    private static final FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
    
    // 简单面样式
    public static Style createSimplePolygonStyle(Color strokeColor, Color fillColor) {
        Stroke stroke = sf.createStroke(
            ff.literal(strokeColor),
            ff.literal(1)
        );
        
        Fill fill = sf.createFill(ff.literal(fillColor));
        
        PolygonSymbolizer polygonSymbolizer = sf.createPolygonSymbolizer();
        polygonSymbolizer.setStroke(stroke);
        polygonSymbolizer.setFill(fill);
        
        return createStyle(polygonSymbolizer);
    }
    
    // 带透明度的样式
    public static Style createTransparentPolygonStyle(Color strokeColor, 
            Color fillColor, double opacity) {
        
        Stroke stroke = sf.createStroke(
            ff.literal(strokeColor),
            ff.literal(1)
        );
        
        Fill fill = sf.createFill(
            ff.literal(fillColor),
            ff.literal(opacity)
        );
        
        PolygonSymbolizer ps = sf.createPolygonSymbolizer();
        ps.setStroke(stroke);
        ps.setFill(fill);
        
        return createStyle(ps);
    }
    
    // 填充图案样式
    public static Style createPatternFillStyle(String pattern, Color color) {
        // 支持的图案: shape://times, shape://plus, etc.
        Mark mark = sf.createMark();
        mark.setWellKnownName(ff.literal(pattern));
        mark.setStroke(sf.createStroke(ff.literal(color), ff.literal(1)));
        
        Graphic graphic = sf.createDefaultGraphic();
        graphic.graphicalSymbols().clear();
        graphic.graphicalSymbols().add(mark);
        graphic.setSize(ff.literal(8));
        
        Fill fill = sf.createFill(ff.literal(Color.WHITE));
        fill.setGraphicFill(graphic);
        
        Stroke stroke = sf.createStroke(ff.literal(color), ff.literal(1));
        
        PolygonSymbolizer ps = sf.createPolygonSymbolizer();
        ps.setStroke(stroke);
        ps.setFill(fill);
        
        return createStyle(ps);
    }
    
    private static Style createStyle(Symbolizer symbolizer) {
        Rule rule = sf.createRule();
        rule.symbolizers().add(symbolizer);
        
        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        fts.rules().add(rule);
        
        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        
        return style;
    }
}

11.5 文本标注

11.5.1 TextSymbolizer

public class TextStyleExample {
    
    private static final StyleFactory sf = CommonFactoryFinder.getStyleFactory();
    private static final FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
    
    // 简单标注
    public static Style createSimpleLabelStyle(String attribute) {
        // 字体
        Font font = sf.createFont(
            ff.literal("宋体"),
            ff.literal("normal"),
            ff.literal("normal"),
            ff.literal(12)
        );
        
        // 标注位置
        AnchorPoint anchor = sf.createAnchorPoint(
            ff.literal(0.5),  // X 锚点
            ff.literal(0.5)   // Y 锚点
        );
        
        Displacement displacement = sf.createDisplacement(
            ff.literal(0),
            ff.literal(5)  // Y 偏移
        );
        
        PointPlacement placement = sf.createPointPlacement(anchor, displacement, 
            ff.literal(0));  // 旋转角度
        
        // 创建文本符号化器
        TextSymbolizer textSymbolizer = sf.createTextSymbolizer();
        textSymbolizer.setLabel(ff.property(attribute));
        textSymbolizer.setFont(font);
        textSymbolizer.setLabelPlacement(placement);
        
        // 设置 Halo(光晕效果)
        Halo halo = sf.createHalo(
            sf.createFill(ff.literal(Color.WHITE)),
            ff.literal(2)
        );
        textSymbolizer.setHalo(halo);
        
        // 设置颜色
        textSymbolizer.setFill(sf.createFill(ff.literal(Color.BLACK)));
        
        return createStyle(textSymbolizer);
    }
    
    // 沿线标注
    public static Style createLineLabelStyle(String attribute) {
        Font font = sf.createFont(
            ff.literal("Arial"),
            ff.literal("normal"),
            ff.literal("normal"),
            ff.literal(10)
        );
        
        // 沿线放置
        LinePlacement linePlacement = sf.createLinePlacement(ff.literal(5));
        linePlacement.setRepeated(true);
        linePlacement.setGap(ff.literal(100));
        
        TextSymbolizer textSymbolizer = sf.createTextSymbolizer();
        textSymbolizer.setLabel(ff.property(attribute));
        textSymbolizer.setFont(font);
        textSymbolizer.setLabelPlacement(linePlacement);
        textSymbolizer.setFill(sf.createFill(ff.literal(Color.BLUE)));
        
        return createStyle(textSymbolizer);
    }
    
    // 组合样式(点+标注)
    public static Style createPointWithLabelStyle(String labelAttribute) {
        // 点样式
        Mark mark = sf.createMark();
        mark.setWellKnownName(ff.literal("circle"));
        mark.setFill(sf.createFill(ff.literal(Color.RED)));
        
        Graphic graphic = sf.createDefaultGraphic();
        graphic.graphicalSymbols().clear();
        graphic.graphicalSymbols().add(mark);
        graphic.setSize(ff.literal(6));
        
        PointSymbolizer pointSymbolizer = sf.createPointSymbolizer();
        pointSymbolizer.setGraphic(graphic);
        
        // 标注样式
        Font font = sf.createFont(
            ff.literal("宋体"),
            ff.literal("normal"),
            ff.literal("normal"),
            ff.literal(12)
        );
        
        PointPlacement placement = sf.createPointPlacement(
            sf.createAnchorPoint(ff.literal(0.5), ff.literal(0)),
            sf.createDisplacement(ff.literal(0), ff.literal(10)),
            ff.literal(0)
        );
        
        TextSymbolizer textSymbolizer = sf.createTextSymbolizer();
        textSymbolizer.setLabel(ff.property(labelAttribute));
        textSymbolizer.setFont(font);
        textSymbolizer.setLabelPlacement(placement);
        textSymbolizer.setFill(sf.createFill(ff.literal(Color.BLACK)));
        
        Rule rule = sf.createRule();
        rule.symbolizers().add(pointSymbolizer);
        rule.symbolizers().add(textSymbolizer);
        
        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        fts.rules().add(rule);
        
        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        
        return style;
    }
    
    private static Style createStyle(Symbolizer symbolizer) {
        Rule rule = sf.createRule();
        rule.symbolizers().add(symbolizer);
        
        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        fts.rules().add(rule);
        
        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        
        return style;
    }
}

11.6 分类样式

11.6.1 按属性分类

public class ClassifiedStyleExample {
    
    private static final StyleFactory sf = CommonFactoryFinder.getStyleFactory();
    private static final FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
    
    // 分类渲染
    public static Style createClassifiedStyle(String attribute, 
            Map<Object, Color> classification) {
        
        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        
        for (Map.Entry<Object, Color> entry : classification.entrySet()) {
            // 创建过滤器
            Filter filter = ff.equals(ff.property(attribute), ff.literal(entry.getKey()));
            
            // 创建样式
            Fill fill = sf.createFill(ff.literal(entry.getValue()));
            Stroke stroke = sf.createStroke(ff.literal(Color.BLACK), ff.literal(1));
            
            PolygonSymbolizer ps = sf.createPolygonSymbolizer();
            ps.setFill(fill);
            ps.setStroke(stroke);
            
            Rule rule = sf.createRule();
            rule.setFilter(filter);
            rule.symbolizers().add(ps);
            rule.setName(entry.getKey().toString());
            
            fts.rules().add(rule);
        }
        
        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        
        return style;
    }
    
    // 数值范围分类
    public static Style createRangeStyle(String attribute, 
            double[] breaks, Color[] colors) {
        
        FeatureTypeStyle fts = sf.createFeatureTypeStyle();
        
        for (int i = 0; i < breaks.length - 1; i++) {
            Filter filter = ff.and(
                ff.greaterOrEqual(ff.property(attribute), ff.literal(breaks[i])),
                ff.less(ff.property(attribute), ff.literal(breaks[i + 1]))
            );
            
            Fill fill = sf.createFill(ff.literal(colors[i]));
            Stroke stroke = sf.createStroke(ff.literal(Color.GRAY), ff.literal(0.5));
            
            PolygonSymbolizer ps = sf.createPolygonSymbolizer();
            ps.setFill(fill);
            ps.setStroke(stroke);
            
            Rule rule = sf.createRule();
            rule.setFilter(filter);
            rule.symbolizers().add(ps);
            rule.setName(String.format("%.0f - %.0f", breaks[i], breaks[i + 1]));
            
            fts.rules().add(rule);
        }
        
        Style style = sf.createStyle();
        style.featureTypeStyles().add(fts);
        
        return style;
    }
}

11.7 SLD 文件操作

11.7.1 读取 SLD

import org.geotools.xml.styling.SLDParser;

public class SLDFileOperations {
    
    // 从文件读取
    public static Style loadFromFile(String path) throws Exception {
        StyleFactory sf = CommonFactoryFinder.getStyleFactory();
        SLDParser parser = new SLDParser(sf, new File(path));
        
        Style[] styles = parser.readXML();
        return styles.length > 0 ? styles[0] : null;
    }
    
    // 从 URL 读取
    public static Style loadFromURL(String url) throws Exception {
        StyleFactory sf = CommonFactoryFinder.getStyleFactory();
        SLDParser parser = new SLDParser(sf, new URL(url));
        
        Style[] styles = parser.readXML();
        return styles.length > 0 ? styles[0] : null;
    }
    
    // 从字符串读取
    public static Style loadFromString(String sld) throws Exception {
        StyleFactory sf = CommonFactoryFinder.getStyleFactory();
        SLDParser parser = new SLDParser(sf, new StringReader(sld));
        
        Style[] styles = parser.readXML();
        return styles.length > 0 ? styles[0] : null;
    }
}

11.7.2 保存 SLD

import org.geotools.xml.styling.SLDTransformer;

public class SLDExport {
    
    // 导出为 SLD 字符串
    public static String exportToString(Style style) throws Exception {
        SLDTransformer transformer = new SLDTransformer();
        transformer.setIndentation(2);
        return transformer.transform(style);
    }
    
    // 导出到文件
    public static void exportToFile(Style style, String path) throws Exception {
        String sld = exportToString(style);
        try (FileWriter writer = new FileWriter(path)) {
            writer.write(sld);
        }
    }
}

11.8 SLD 工具类

11.8.1 快速创建样式

import org.geotools.styling.SLD;

public class SLDUtilityExample {
    
    public static void quickStyles() {
        // 快速创建点样式
        Style pointStyle = SLD.createPointStyle("circle", Color.RED, Color.BLACK, 
            0.5f, 10);
        
        // 快速创建线样式
        Style lineStyle = SLD.createLineStyle(Color.BLUE, 2);
        
        // 快速创建面样式
        Style polygonStyle = SLD.createPolygonStyle(Color.BLACK, Color.YELLOW, 0.5f);
        
        // 从要素类型创建默认样式
        // Style defaultStyle = SLD.createSimpleStyle(featureType);
    }
}

11.9 本章小结

本章详细介绍了 GeoTools 的样式系统:

  1. SLD 规范

    • 结构层次
    • 样式工厂
  2. 符号化器

    • PointSymbolizer
    • LineSymbolizer
    • PolygonSymbolizer
    • TextSymbolizer
  3. 分类样式

    • 属性分类
    • 数值范围
  4. SLD 文件

    • 读取和保存
    • SLD 工具类

关键要点

  • 使用 StyleFactory 创建样式
  • 符号化器定义具体的可视化效果
  • Rule 可以包含过滤器实现分类渲染
  • SLD 文件可以持久化样式配置

← 上一章:坐标参考系统与投影转换 | 返回目录 | 下一章:地图渲染与输出 →

posted @ 2025-12-29 11:40  我才是银古  阅读(5)  评论(0)    收藏  举报