第06章-数据提供者详解

第06章:数据提供者详解

6.1 数据提供者概述

6.1.1 IProvider 接口

数据提供者是 SharpMap 访问空间数据的核心组件,所有数据提供者都实现 IProvider 接口:

namespace SharpMap.Data.Providers
{
    public interface IProvider : IDisposable
    {
        // 连接管理
        string ConnectionID { get; }
        bool IsOpen { get; }
        void Open();
        void Close();
        
        // 空间参考
        int SRID { get; set; }
        
        // 数据范围
        Envelope GetExtents();
        
        // 几何查询
        Collection<Geometry> GetGeometriesInView(Envelope envelope);
        Collection<uint> GetObjectIDsInView(Envelope envelope);
        Geometry GetGeometryByID(uint oid);
        
        // 要素查询
        void ExecuteIntersectionQuery(Envelope envelope, FeatureDataSet ds);
        void ExecuteIntersectionQuery(Geometry geometry, FeatureDataSet ds);
        FeatureDataRow GetFeature(uint oid);
        
        // 要素计数
        int GetFeatureCount();
    }
}

6.1.2 内置数据提供者

SharpMap 提供了多种内置数据提供者:

提供者 数据格式
ShapeFile ESRI Shapefile SharpMap
PostGIS PostgreSQL/PostGIS SharpMap
MsSql SQL Server Spatial SharpMap
MsSqlSpatial SQL Server Geometry SharpMap
Oracle Oracle Spatial SharpMap
Spatialite Spatialite SharpMap
Ogr GDAL/OGR 支持的格式 SharpMap.Extensions
WMS WMS 服务 SharpMap
GeoPackage OGC GeoPackage SharpMap.Extensions

6.2 ShapeFile 数据提供者

6.2.1 基本使用

using SharpMap.Data.Providers;

// 创建 ShapeFile 提供者
var provider = new ShapeFile("data/countries.shp");

// 使用文件索引(.shx 文件)
var provider2 = new ShapeFile("data/countries.shp", true);

// 使用空间索引(自动创建或读取 .sidx 文件)
var provider3 = new ShapeFile("data/countries.shp", true, true);

// 基本操作
provider.Open();
Console.WriteLine($"要素数量:{provider.GetFeatureCount()}");
Console.WriteLine($"数据范围:{provider.GetExtents()}");
Console.WriteLine($"SRID:{provider.SRID}");
provider.Close();

6.2.2 读取属性结构

var provider = new ShapeFile("data/countries.shp", true);
provider.Open();

// 获取 DBF 文件的字段结构
var table = new FeatureDataTable();
provider.ExecuteIntersectionQuery(provider.GetExtents(), new FeatureDataSet { Tables = { table } });

foreach (DataColumn column in table.Columns)
{
    Console.WriteLine($"字段:{column.ColumnName}");
    Console.WriteLine($"  类型:{column.DataType}");
    Console.WriteLine($"  允许空:{column.AllowDBNull}");
}

provider.Close();

6.2.3 查询数据

var provider = new ShapeFile("data/countries.shp", true);
provider.Open();

// 范围查询
var envelope = new Envelope(-180, 180, -90, 90);
var geometries = provider.GetGeometriesInView(envelope);
Console.WriteLine($"范围内几何数量:{geometries.Count}");

// 获取要素数据
var featureDataSet = new FeatureDataSet();
provider.ExecuteIntersectionQuery(envelope, featureDataSet);

if (featureDataSet.Tables.Count > 0)
{
    var table = featureDataSet.Tables[0];
    foreach (FeatureDataRow row in table.Rows)
    {
        Console.WriteLine($"名称:{row["NAME"]}");
        Console.WriteLine($"几何类型:{row.Geometry.GeometryType}");
    }
}

// 按 ID 获取单个要素
var feature = provider.GetFeature(0);
if (feature != null)
{
    Console.WriteLine($"第一个要素:{feature.Geometry.AsText()}");
}

provider.Close();

6.2.4 空间索引

// 创建空间索引
public static void CreateSpatialIndex(string shapefile)
{
    var provider = new ShapeFile(shapefile, true, true);
    provider.Open();
    
    // 如果没有索引,SharpMap 会自动创建
    // 索引文件保存为 .sidx
    
    provider.Close();
}

// 手动创建索引
public static void RebuildSpatialIndex(string shapefile)
{
    var indexFile = Path.ChangeExtension(shapefile, ".sidx");
    if (File.Exists(indexFile))
    {
        File.Delete(indexFile);
    }
    
    // 重新创建索引
    var provider = new ShapeFile(shapefile, true, true);
    provider.Open();
    provider.Close();
}

6.2.5 编码设置

// 设置 DBF 文件编码(处理中文等非 ASCII 字符)
var provider = new ShapeFile("data/china.shp", true);
provider.Encoding = Encoding.GetEncoding("GB2312");  // 或 "GBK"

// 或使用 UTF-8
provider.Encoding = Encoding.UTF8;

6.3 PostGIS 数据提供者

6.3.1 基本配置

using SharpMap.Data.Providers;

// 连接字符串
string connectionString = "Host=localhost;Port=5432;Database=gisdb;Username=postgres;Password=123456";

// 创建 PostGIS 提供者
// 参数:连接字符串, 表名, 几何列, 主键列
var provider = new PostGIS(connectionString, "countries", "geom", "gid");

// 设置 SRID
provider.SRID = 4326;

// 使用
var layer = new VectorLayer("PostGIS Countries", provider);
map.Layers.Add(layer);

6.3.2 高级配置

// 创建带过滤条件的提供者
var provider = new PostGIS(connectionString, "countries", "geom", "gid")
{
    SRID = 4326,
    DefinitionQuery = "population > 1000000"  // SQL WHERE 条件
};

// 指定要返回的字段
provider.DefinitionQuery = "continent = 'Asia'";

// 使用视图
var viewProvider = new PostGIS(connectionString, "v_large_countries", "geom", "gid");

// 使用 SQL 查询
var sqlProvider = new PostGIS(
    connectionString,
    "(SELECT gid, name, geom FROM countries WHERE population > 1000000) AS subquery",
    "geom",
    "gid");

6.3.3 PostGIS 函数

// 使用 PostGIS 函数查询
public static FeatureDataTable QueryWithinDistance(
    string connectionString, 
    Coordinate center, 
    double distanceMeters)
{
    using (var conn = new NpgsqlConnection(connectionString))
    {
        conn.Open();
        
        string sql = @"
            SELECT *, ST_Distance(geom::geography, ST_MakePoint(@x, @y)::geography) as distance
            FROM cities
            WHERE ST_DWithin(geom::geography, ST_MakePoint(@x, @y)::geography, @distance)
            ORDER BY distance";
        
        using (var cmd = new NpgsqlCommand(sql, conn))
        {
            cmd.Parameters.AddWithValue("x", center.X);
            cmd.Parameters.AddWithValue("y", center.Y);
            cmd.Parameters.AddWithValue("distance", distanceMeters);
            
            var table = new DataTable();
            using (var adapter = new NpgsqlDataAdapter(cmd))
            {
                adapter.Fill(table);
            }
            
            return ConvertToFeatureDataTable(table);
        }
    }
}

6.4 SQL Server 数据提供者

6.4.1 MsSql 提供者

using SharpMap.Data.Providers;

// 连接字符串
string connectionString = @"Server=localhost;Database=GisDB;Trusted_Connection=True;";

// 创建 SQL Server 提供者
var provider = new MsSql(connectionString, "dbo.Countries", "Shape", "ObjectId");

// 设置 SRID
provider.SRID = 4326;

// 添加 WHERE 条件
provider.DefinitionQuery = "Status = 'Active'";

// 使用
var layer = new VectorLayer("SQL Server Data", provider);

6.4.2 MsSqlSpatial 提供者

// 使用 SQL Server 的 geometry 数据类型
var provider = new MsSqlSpatial(connectionString, "dbo.Parcels", "Shape", "ParcelId");

// 设置几何类型(geography 或 geometry)
provider.GeometryColumnType = SqlGeometryType.Geometry;

// 添加空间索引提示
provider.SpatialHint = "IX_Spatial_Shape";

6.5 OGR 数据提供者

6.5.1 基本使用

// 需要安装 SharpMap.Extensions 和 GDAL
using SharpMap.Data.Providers;

// 打开 GeoJSON 文件
var geoJsonProvider = new Ogr("data/countries.geojson");

// 打开 KML 文件
var kmlProvider = new Ogr("data/places.kml");

// 打开 GML 文件
var gmlProvider = new Ogr("data/features.gml");

// 打开 GPX 文件
var gpxProvider = new Ogr("data/track.gpx");

// 打开 FileGDB(ESRI 文件地理数据库)
var fgdbProvider = new Ogr("data/geodatabase.gdb", 0);  // 0 为图层索引

6.5.2 OGR 图层选择

// 打开数据源
var ogrProvider = new Ogr("data/complex.gdb");

// 获取图层列表
var layerNames = Ogr.GetLayerNames("data/complex.gdb");
foreach (var name in layerNames)
{
    Console.WriteLine($"图层:{name}");
}

// 按名称打开图层
var namedProvider = new Ogr("data/complex.gdb", "buildings");

// 按索引打开图层
var indexProvider = new Ogr("data/complex.gdb", 2);

6.5.3 支持的格式

OGR 支持众多数据格式,常用的包括:

格式 扩展名 说明
GeoJSON .geojson, .json JSON 格式的矢量数据
KML .kml Google Earth 格式
GML .gml OGC GML 格式
GPX .gpx GPS 交换格式
FileGDB .gdb ESRI 文件地理数据库
GeoPackage .gpkg OGC GeoPackage
CSV .csv 带坐标的 CSV 文件
DXF .dxf AutoCAD 交换格式
MapInfo .tab MapInfo 格式
SpatiaLite .sqlite SQLite 空间数据库

6.6 内存数据提供者

6.6.1 GeometryProvider

using SharpMap.Data.Providers;
using NetTopologySuite.Geometries;

// 创建几何集合
var geometries = new List<Geometry>();
var factory = new GeometryFactory();

// 添加点
geometries.Add(factory.CreatePoint(new Coordinate(116.4, 39.9)));
geometries.Add(factory.CreatePoint(new Coordinate(121.5, 31.2)));
geometries.Add(factory.CreatePoint(new Coordinate(113.3, 23.1)));

// 添加线
geometries.Add(factory.CreateLineString(new[]
{
    new Coordinate(116.4, 39.9),
    new Coordinate(121.5, 31.2),
    new Coordinate(113.3, 23.1)
}));

// 创建提供者
var provider = new GeometryProvider(geometries);

// 使用
var layer = new VectorLayer("Memory Data", provider);

6.6.2 GeometryFeatureProvider

using SharpMap.Data.Providers;
using SharpMap.Data;
using NetTopologySuite.Geometries;

// 创建要素集合
var features = new List<Feature>();
var factory = new GeometryFactory();

// 创建要素(带属性)
var feature1 = new Feature
{
    Geometry = factory.CreatePoint(new Coordinate(116.4, 39.9)),
    Attributes = new AttributesTable
    {
        { "Name", "北京" },
        { "Population", 21540000 }
    }
};

var feature2 = new Feature
{
    Geometry = factory.CreatePoint(new Coordinate(121.5, 31.2)),
    Attributes = new AttributesTable
    {
        { "Name", "上海" },
        { "Population", 24280000 }
    }
};

features.Add(feature1);
features.Add(feature2);

// 创建提供者
var provider = new GeometryFeatureProvider(features);

// 使用
var layer = new VectorLayer("Feature Data", provider);

6.6.3 DataTablePoint 提供者

using SharpMap.Data.Providers;

// 创建数据表
var dataTable = new DataTable();
dataTable.Columns.Add("ID", typeof(int));
dataTable.Columns.Add("Name", typeof(string));
dataTable.Columns.Add("X", typeof(double));
dataTable.Columns.Add("Y", typeof(double));

// 添加数据
dataTable.Rows.Add(1, "北京", 116.4, 39.9);
dataTable.Rows.Add(2, "上海", 121.5, 31.2);
dataTable.Rows.Add(3, "广州", 113.3, 23.1);

// 创建点数据提供者
var provider = new DataTablePoint(dataTable, "ID", "X", "Y");

// 使用
var layer = new VectorLayer("Cities", provider);
layer.Style = new VectorStyle
{
    PointColor = Brushes.Red,
    PointSize = 10
};

6.7 自定义数据提供者

6.7.1 基本实现

using System.Collections.ObjectModel;
using SharpMap.Data;
using SharpMap.Data.Providers;
using NetTopologySuite.Geometries;

public class CustomProvider : IProvider
{
    private readonly List<Feature> _features;
    private readonly GeometryFactory _factory;
    private bool _isOpen;
    
    public CustomProvider()
    {
        _features = new List<Feature>();
        _factory = new GeometryFactory();
    }
    
    public string ConnectionID => "CustomProvider";
    public bool IsOpen => _isOpen;
    public int SRID { get; set; } = 4326;
    
    public void Open() => _isOpen = true;
    public void Close() => _isOpen = false;
    
    // 添加要素
    public void AddFeature(Geometry geometry, IDictionary<string, object> attributes)
    {
        var feature = new Feature
        {
            Geometry = geometry,
            Attributes = new AttributesTable(attributes)
        };
        _features.Add(feature);
    }
    
    public Envelope GetExtents()
    {
        if (_features.Count == 0)
            return new Envelope();
            
        var env = new Envelope();
        foreach (var feature in _features)
        {
            env.ExpandToInclude(feature.Geometry.EnvelopeInternal);
        }
        return env;
    }
    
    public Collection<Geometry> GetGeometriesInView(Envelope envelope)
    {
        var result = new Collection<Geometry>();
        foreach (var feature in _features)
        {
            if (envelope.Intersects(feature.Geometry.EnvelopeInternal))
            {
                result.Add(feature.Geometry);
            }
        }
        return result;
    }
    
    public Collection<uint> GetObjectIDsInView(Envelope envelope)
    {
        var result = new Collection<uint>();
        for (int i = 0; i < _features.Count; i++)
        {
            if (envelope.Intersects(_features[i].Geometry.EnvelopeInternal))
            {
                result.Add((uint)i);
            }
        }
        return result;
    }
    
    public Geometry GetGeometryByID(uint oid)
    {
        if (oid < _features.Count)
            return _features[(int)oid].Geometry;
        return null;
    }
    
    public void ExecuteIntersectionQuery(Envelope envelope, FeatureDataSet ds)
    {
        var table = CreateFeatureDataTable();
        
        for (int i = 0; i < _features.Count; i++)
        {
            if (envelope.Intersects(_features[i].Geometry.EnvelopeInternal))
            {
                AddFeatureToTable(table, i);
            }
        }
        
        ds.Tables.Add(table);
    }
    
    public void ExecuteIntersectionQuery(Geometry geometry, FeatureDataSet ds)
    {
        var table = CreateFeatureDataTable();
        
        for (int i = 0; i < _features.Count; i++)
        {
            if (geometry.Intersects(_features[i].Geometry))
            {
                AddFeatureToTable(table, i);
            }
        }
        
        ds.Tables.Add(table);
    }
    
    public FeatureDataRow GetFeature(uint oid)
    {
        if (oid >= _features.Count)
            return null;
            
        var table = CreateFeatureDataTable();
        AddFeatureToTable(table, (int)oid);
        return table.Rows[0] as FeatureDataRow;
    }
    
    public int GetFeatureCount() => _features.Count;
    
    private FeatureDataTable CreateFeatureDataTable()
    {
        var table = new FeatureDataTable();
        
        if (_features.Count > 0)
        {
            foreach (var name in _features[0].Attributes.GetNames())
            {
                var value = _features[0].Attributes[name];
                table.Columns.Add(name, value?.GetType() ?? typeof(object));
            }
        }
        
        return table;
    }
    
    private void AddFeatureToTable(FeatureDataTable table, int index)
    {
        var feature = _features[index];
        var row = table.NewRow();
        row.Geometry = feature.Geometry;
        
        foreach (var name in feature.Attributes.GetNames())
        {
            row[name] = feature.Attributes[name];
        }
        
        table.AddRow(row);
    }
    
    public void Dispose()
    {
        _features.Clear();
    }
}

6.7.2 使用自定义提供者

// 创建自定义提供者
var provider = new CustomProvider();

// 添加数据
var factory = new GeometryFactory();

provider.AddFeature(
    factory.CreatePoint(new Coordinate(116.4, 39.9)),
    new Dictionary<string, object> 
    { 
        { "Name", "北京" }, 
        { "Type", "Capital" } 
    });

provider.AddFeature(
    factory.CreatePoint(new Coordinate(121.5, 31.2)),
    new Dictionary<string, object> 
    { 
        { "Name", "上海" }, 
        { "Type", "Municipality" } 
    });

// 创建图层
var layer = new VectorLayer("Custom Data", provider);
layer.Style = new VectorStyle
{
    PointColor = Brushes.Blue,
    PointSize = 12
};

map.Layers.Add(layer);

6.7.3 实时数据提供者

public class RealtimeProvider : IProvider
{
    private readonly Func<IEnumerable<Feature>> _dataSource;
    private List<Feature> _cachedFeatures;
    private DateTime _lastUpdate;
    private readonly TimeSpan _cacheTimeout;
    
    public RealtimeProvider(Func<IEnumerable<Feature>> dataSource, TimeSpan? cacheTimeout = null)
    {
        _dataSource = dataSource;
        _cacheTimeout = cacheTimeout ?? TimeSpan.FromSeconds(5);
        _cachedFeatures = new List<Feature>();
    }
    
    private void RefreshCache()
    {
        if (DateTime.Now - _lastUpdate > _cacheTimeout)
        {
            _cachedFeatures = _dataSource().ToList();
            _lastUpdate = DateTime.Now;
        }
    }
    
    public Collection<Geometry> GetGeometriesInView(Envelope envelope)
    {
        RefreshCache();
        
        var result = new Collection<Geometry>();
        foreach (var feature in _cachedFeatures)
        {
            if (envelope.Intersects(feature.Geometry.EnvelopeInternal))
            {
                result.Add(feature.Geometry);
            }
        }
        return result;
    }
    
    // ... 其他接口方法实现类似
}

// 使用实时数据提供者
var realtimeProvider = new RealtimeProvider(() =>
{
    // 从 API 或数据库获取实时数据
    return FetchVehiclePositions();
});

var layer = new VectorLayer("Vehicles", realtimeProvider);
map.VariableLayers.Add(layer);  // 添加到动态图层集合

6.8 数据提供者性能优化

6.8.1 使用空间索引

// ShapeFile 使用空间索引
var provider = new ShapeFile("data.shp", true, true);  // 第三个参数启用空间索引

// PostGIS 确保有空间索引
// CREATE INDEX idx_geom ON table USING GIST(geom);

// SQL Server 确保有空间索引
// CREATE SPATIAL INDEX idx_geom ON table(geom);

6.8.2 限制返回字段

// PostGIS 只返回需要的字段
var provider = new PostGIS(connectionString, 
    "(SELECT gid, name, geom FROM countries) AS subquery", 
    "geom", "gid");

// 减少网络传输和内存使用

6.8.3 使用数据缓存

public class CachedProvider : IProvider
{
    private readonly IProvider _innerProvider;
    private readonly Dictionary<string, object> _cache;
    private readonly TimeSpan _cacheExpiration;
    
    public CachedProvider(IProvider innerProvider, TimeSpan? expiration = null)
    {
        _innerProvider = innerProvider;
        _cache = new Dictionary<string, object>();
        _cacheExpiration = expiration ?? TimeSpan.FromMinutes(5);
    }
    
    public Envelope GetExtents()
    {
        string key = "extents";
        if (!_cache.ContainsKey(key))
        {
            _cache[key] = _innerProvider.GetExtents();
        }
        return (Envelope)_cache[key];
    }
    
    // ... 实现其他缓存逻辑
}

6.9 本章小结

本章详细介绍了 SharpMap 的数据提供者系统:

  1. IProvider 接口:了解了数据提供者的核心接口定义
  2. ShapeFile:掌握了 Shapefile 文件的读取和查询
  3. PostGIS:学习了 PostGIS 数据库的连接和使用
  4. SQL Server:了解了 SQL Server 空间数据的访问
  5. OGR:掌握了通过 OGR 访问多种数据格式
  6. 内存提供者:学习了内存数据的使用方法
  7. 自定义提供者:了解了如何实现自定义数据提供者
  8. 性能优化:掌握了数据提供者的优化技巧

6.10 参考资源


下一章预告:第07章将详细介绍 SharpMap 的样式系统和主题渲染,包括唯一值渲染、分级渲染等高级样式配置。

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