第02章-环境配置与项目创建

第02章:环境配置与项目创建

2.1 开发环境准备

2.1.1 .NET 版本选择

SharpMap 支持多种 .NET 版本:

.NET 版本 支持情况 建议
.NET Framework 4.6.1+ 完整支持 推荐用于 WinForms 应用
.NET Framework 4.7.2+ 完整支持 推荐用于 WPF 应用
.NET Core 3.1 部分支持 服务端渲染
.NET 5.0+ 部分支持 服务端渲染
.NET 6.0+ 部分支持 需要 Windows 兼容包

注意:SharpMap.UI 组件(MapBox、MapImage)依赖 Windows Forms,因此在非 Windows 平台上只能进行服务端地图渲染。

2.1.2 Visual Studio 配置

推荐版本:Visual Studio 2019 或 Visual Studio 2022

必需的工作负载

  1. .NET 桌面开发

    • Windows Forms 应用开发
    • WPF 应用开发
  2. ASP.NET 和 Web 开发(可选)

    • 用于 Web 地图服务开发

安装步骤

  1. 打开 Visual Studio Installer
  2. 选择"修改"当前安装
  3. 勾选以下工作负载:
    • .NET 桌面开发
    • ASP.NET 和 Web 开发(可选)
  4. 确保已安装 .NET Framework 4.7.2 目标包

2.1.3 其他开发工具

JetBrains Rider

  • 跨平台 .NET IDE
  • 支持 SharpMap 开发
  • NuGet 包管理集成

Visual Studio Code

  • 轻量级编辑器
  • 需要 C# 扩展
  • 适合简单项目开发
# VS Code C# 扩展安装
code --install-extension ms-dotnettools.csharp

2.1.4 必需的系统组件

Windows 系统组件

  1. GDI+:用于图形渲染(Windows 自带)
  2. Visual C++ 运行库:如果使用 GDAL 扩展

可选组件

  1. GDAL 库:用于栅格数据支持
  2. PostgreSQL/PostGIS:用于空间数据库访问

2.2 创建 WinForms 项目

2.2.1 使用 Visual Studio 创建项目

步骤 1:创建新项目

  1. 打开 Visual Studio
  2. 选择"创建新项目"
  3. 选择"Windows 窗体应用 (.NET Framework)"
  4. 点击"下一步"

步骤 2:配置项目

  • 项目名称:SharpMapDemo
  • 位置:选择适当的文件夹
  • 框架:.NET Framework 4.7.2
  • 点击"创建"

2.2.2 安装 NuGet 包

通过 NuGet 包管理器

  1. 右键点击项目 → "管理 NuGet 程序包"
  2. 搜索并安装以下包:
    • SharpMap
    • SharpMap.UI

通过包管理器控制台

Install-Package SharpMap
Install-Package SharpMap.UI

2.2.3 添加 MapBox 控件到工具箱

步骤 1:打开工具箱

  1. 在 Visual Studio 中,打开"视图" → "工具箱"
  2. 右键点击工具箱空白处
  3. 选择"选择项..."

步骤 2:浏览 SharpMap.UI.dll

  1. 在"选择工具箱项"对话框中,点击"浏览..."
  2. 导航到 NuGet 包目录:
    packages\SharpMap.UI.1.2.0\lib\net461\SharpMap.UI.dll
    
  3. 选择该 DLL 文件并点击"打开"

步骤 3:确认添加

  1. 确认 MapBox 和 MapImage 控件已被勾选
  2. 点击"确定"

现在您可以在工具箱中看到 MapBox 和 MapImage 控件了。

2.2.4 创建第一个地图窗体

步骤 1:设计窗体

  1. 打开 Form1.cs [设计]
  2. 从工具箱拖拽 MapBox 控件到窗体
  3. 设置 MapBox 的 Dock 属性为 Fill
  4. 调整窗体大小(如 1024 x 768)

步骤 2:编写代码

using System;
using System.Drawing;
using System.Windows.Forms;
using SharpMap;
using SharpMap.Layers;
using SharpMap.Data.Providers;
using SharpMap.Styles;

namespace SharpMapDemo
{
    public partial class Form1 : Form
    {
        private Map _map;

        public Form1()
        {
            InitializeComponent();
            InitializeMap();
        }

        private void InitializeMap()
        {
            // 创建地图对象
            _map = new Map(mapBox1.Size);
            _map.BackColor = Color.White;

            // 创建矢量图层
            var layer = new VectorLayer("States");
            
            // 设置数据源(请替换为实际的 Shapefile 路径)
            string shapeFilePath = @"C:\Data\states.shp";
            if (System.IO.File.Exists(shapeFilePath))
            {
                layer.DataSource = new ShapeFile(shapeFilePath, true);
                
                // 设置样式
                layer.Style = new VectorStyle
                {
                    Fill = new SolidBrush(Color.LightGreen),
                    Outline = new Pen(Color.DarkGreen, 1),
                    EnableOutline = true
                };
                
                // 添加图层到地图
                _map.Layers.Add(layer);
                
                // 缩放到全图范围
                _map.ZoomToExtents();
            }
            else
            {
                MessageBox.Show("请配置正确的 Shapefile 路径!", "提示", 
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }

            // 将地图绑定到 MapBox 控件
            mapBox1.Map = _map;
            
            // 设置交互模式
            mapBox1.ActiveTool = SharpMap.Forms.MapBox.Tools.Pan;
        }
    }
}

2.2.5 项目文件结构

完整的项目结构应该如下:

SharpMapDemo/
├── SharpMapDemo.csproj
├── App.config
├── packages.config
├── Program.cs
├── Form1.cs
├── Form1.Designer.cs
├── Form1.resx
├── Properties/
│   ├── AssemblyInfo.cs
│   ├── Resources.resx
│   ├── Resources.Designer.cs
│   ├── Settings.settings
│   └── Settings.Designer.cs
└── Data/
    ├── states.shp
    ├── states.shx
    ├── states.dbf
    └── states.prj

2.3 创建 WPF 项目

2.3.1 创建 WPF 项目

步骤 1:创建新项目

  1. 选择"WPF 应用程序 (.NET Framework)"
  2. 项目名称:SharpMapWpfDemo
  3. 框架:.NET Framework 4.7.2

步骤 2:安装 NuGet 包

Install-Package SharpMap

2.3.2 创建地图控件

由于 SharpMap 没有原生的 WPF 控件,我们需要使用 WindowsFormsHost 来承载 WinForms 的 MapBox 控件。

步骤 1:添加引用

在项目中添加以下引用:

  • WindowsFormsIntegration
  • System.Windows.Forms

步骤 2:修改 MainWindow.xaml

<Window x:Class="SharpMapWpfDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
        xmlns:wfi="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
        xmlns:sm="clr-namespace:SharpMap.Forms;assembly=SharpMap.UI"
        Title="SharpMap WPF Demo" Height="600" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        
        <!-- 工具栏 -->
        <ToolBar Grid.Row="0">
            <Button Content="平移" Click="Pan_Click"/>
            <Button Content="放大" Click="ZoomIn_Click"/>
            <Button Content="缩小" Click="ZoomOut_Click"/>
            <Button Content="全图" Click="ZoomToExtents_Click"/>
        </ToolBar>
        
        <!-- 地图控件 -->
        <WindowsFormsHost Grid.Row="1" x:Name="mapHost">
            <sm:MapBox x:Name="mapBox"/>
        </WindowsFormsHost>
        
        <!-- 状态栏 -->
        <StatusBar Grid.Row="2">
            <StatusBarItem>
                <TextBlock x:Name="statusText" Text="就绪"/>
            </StatusBarItem>
        </StatusBar>
    </Grid>
</Window>

步骤 3:编写代码 MainWindow.xaml.cs

using System;
using System.Drawing;
using System.Windows;
using SharpMap;
using SharpMap.Layers;
using SharpMap.Data.Providers;
using SharpMap.Styles;

namespace SharpMapWpfDemo
{
    public partial class MainWindow : Window
    {
        private Map _map;

        public MainWindow()
        {
            InitializeComponent();
            InitializeMap();
        }

        private void InitializeMap()
        {
            // 创建地图
            _map = new Map(new System.Drawing.Size(800, 600));
            _map.BackColor = System.Drawing.Color.White;

            // 添加图层
            string shapeFilePath = @"C:\Data\states.shp";
            if (System.IO.File.Exists(shapeFilePath))
            {
                var layer = new VectorLayer("States");
                layer.DataSource = new ShapeFile(shapeFilePath, true);
                layer.Style = new VectorStyle
                {
                    Fill = new SolidBrush(System.Drawing.Color.LightBlue),
                    Outline = new System.Drawing.Pen(System.Drawing.Color.DarkBlue, 1),
                    EnableOutline = true
                };
                _map.Layers.Add(layer);
                _map.ZoomToExtents();
            }

            // 绑定到控件
            mapBox.Map = _map;
            mapBox.ActiveTool = SharpMap.Forms.MapBox.Tools.Pan;
            
            // 监听鼠标移动事件
            mapBox.MouseMove += MapBox_MouseMove;
        }

        private void MapBox_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (_map != null)
            {
                var worldPos = _map.ImageToWorld(new System.Drawing.PointF(e.X, e.Y));
                statusText.Text = $"坐标: {worldPos.X:F6}, {worldPos.Y:F6}";
            }
        }

        private void Pan_Click(object sender, RoutedEventArgs e)
        {
            mapBox.ActiveTool = SharpMap.Forms.MapBox.Tools.Pan;
        }

        private void ZoomIn_Click(object sender, RoutedEventArgs e)
        {
            mapBox.ActiveTool = SharpMap.Forms.MapBox.Tools.ZoomIn;
        }

        private void ZoomOut_Click(object sender, RoutedEventArgs e)
        {
            mapBox.ActiveTool = SharpMap.Forms.MapBox.Tools.ZoomOut;
        }

        private void ZoomToExtents_Click(object sender, RoutedEventArgs e)
        {
            _map.ZoomToExtents();
            mapBox.Refresh();
        }
    }
}

2.4 创建控制台项目

2.4.1 创建项目

控制台项目适用于服务端地图渲染和批处理任务。

# 使用 dotnet CLI 创建项目
dotnet new console -n SharpMapConsole -f net6.0

# 进入项目目录
cd SharpMapConsole

# 添加 SharpMap 包
dotnet add package SharpMap

# 添加 System.Drawing.Common(.NET Core/5+ 需要)
dotnet add package System.Drawing.Common

2.4.2 基本地图渲染示例

using System;
using System.Drawing;
using System.Drawing.Imaging;
using SharpMap;
using SharpMap.Layers;
using SharpMap.Data.Providers;
using SharpMap.Styles;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("SharpMap 控制台示例");
        
        // 创建地图
        var map = new Map(new Size(1024, 768));
        map.BackColor = Color.White;
        
        // 添加矢量图层
        string shapeFilePath = args.Length > 0 ? args[0] : "data/states.shp";
        
        if (!System.IO.File.Exists(shapeFilePath))
        {
            Console.WriteLine($"错误:找不到文件 {shapeFilePath}");
            return;
        }
        
        var layer = new VectorLayer("Data");
        layer.DataSource = new ShapeFile(shapeFilePath, true);
        layer.Style = new VectorStyle
        {
            Fill = new SolidBrush(Color.LightYellow),
            Outline = new Pen(Color.Black, 1),
            EnableOutline = true
        };
        
        map.Layers.Add(layer);
        
        // 缩放到全图
        map.ZoomToExtents();
        
        // 渲染地图
        var image = map.GetMap();
        
        // 保存图片
        string outputPath = "output.png";
        image.Save(outputPath, ImageFormat.Png);
        
        Console.WriteLine($"地图已保存到:{outputPath}");
        Console.WriteLine($"地图范围:{map.Envelope}");
        Console.WriteLine($"图层数量:{map.Layers.Count}");
    }
}

2.4.3 批量处理示例

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using SharpMap;
using SharpMap.Layers;
using SharpMap.Data.Providers;
using SharpMap.Styles;
using NetTopologySuite.Geometries;

class BatchProcessor
{
    static void Main(string[] args)
    {
        string inputDir = args.Length > 0 ? args[0] : "input";
        string outputDir = args.Length > 1 ? args[1] : "output";
        
        // 确保输出目录存在
        Directory.CreateDirectory(outputDir);
        
        // 获取所有 Shapefile
        var shapeFiles = Directory.GetFiles(inputDir, "*.shp");
        
        Console.WriteLine($"找到 {shapeFiles.Length} 个 Shapefile");
        
        foreach (var shapeFile in shapeFiles)
        {
            try
            {
                ProcessShapefile(shapeFile, outputDir);
                Console.WriteLine($"处理完成:{Path.GetFileName(shapeFile)}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"处理失败:{Path.GetFileName(shapeFile)} - {ex.Message}");
            }
        }
        
        Console.WriteLine("批处理完成!");
    }
    
    static void ProcessShapefile(string shapeFilePath, string outputDir)
    {
        var map = new Map(new Size(800, 600));
        map.BackColor = Color.White;
        
        var layer = new VectorLayer("Data");
        layer.DataSource = new ShapeFile(shapeFilePath, true);
        layer.Style = new VectorStyle
        {
            Fill = new SolidBrush(Color.LightGreen),
            Outline = new Pen(Color.DarkGreen, 1),
            EnableOutline = true
        };
        
        map.Layers.Add(layer);
        map.ZoomToExtents();
        
        var image = map.GetMap();
        
        string outputFileName = Path.GetFileNameWithoutExtension(shapeFilePath) + ".png";
        string outputPath = Path.Combine(outputDir, outputFileName);
        
        image.Save(outputPath, ImageFormat.Png);
    }
}

2.5 创建 ASP.NET 项目

2.5.1 创建 ASP.NET Core Web API 项目

dotnet new webapi -n SharpMapWebApi
cd SharpMapWebApi
dotnet add package SharpMap
dotnet add package System.Drawing.Common

2.5.2 创建地图服务控制器

using Microsoft.AspNetCore.Mvc;
using System.Drawing;
using System.Drawing.Imaging;
using SharpMap;
using SharpMap.Layers;
using SharpMap.Data.Providers;
using SharpMap.Styles;
using NetTopologySuite.Geometries;

namespace SharpMapWebApi.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class MapController : ControllerBase
    {
        private readonly string _dataPath;
        
        public MapController(IConfiguration configuration)
        {
            _dataPath = configuration["MapData:Path"] ?? "Data";
        }
        
        /// <summary>
        /// 获取地图图片
        /// </summary>
        [HttpGet("image")]
        public IActionResult GetMapImage(
            [FromQuery] int width = 800, 
            [FromQuery] int height = 600,
            [FromQuery] double? minX = null,
            [FromQuery] double? minY = null,
            [FromQuery] double? maxX = null,
            [FromQuery] double? maxY = null)
        {
            try
            {
                var map = CreateMap(width, height);
                
                // 如果指定了范围,则使用指定范围
                if (minX.HasValue && minY.HasValue && maxX.HasValue && maxY.HasValue)
                {
                    map.ZoomToBox(new Envelope(minX.Value, maxX.Value, minY.Value, maxY.Value));
                }
                else
                {
                    map.ZoomToExtents();
                }
                
                var image = map.GetMap();
                
                using (var stream = new MemoryStream())
                {
                    image.Save(stream, ImageFormat.Png);
                    return File(stream.ToArray(), "image/png");
                }
            }
            catch (Exception ex)
            {
                return BadRequest(new { error = ex.Message });
            }
        }
        
        /// <summary>
        /// 获取地图范围
        /// </summary>
        [HttpGet("extent")]
        public IActionResult GetMapExtent()
        {
            try
            {
                var map = CreateMap(100, 100);
                map.ZoomToExtents();
                
                return Ok(new
                {
                    minX = map.Envelope.MinX,
                    minY = map.Envelope.MinY,
                    maxX = map.Envelope.MaxX,
                    maxY = map.Envelope.MaxY
                });
            }
            catch (Exception ex)
            {
                return BadRequest(new { error = ex.Message });
            }
        }
        
        /// <summary>
        /// 获取图层列表
        /// </summary>
        [HttpGet("layers")]
        public IActionResult GetLayers()
        {
            try
            {
                var shapeFiles = Directory.GetFiles(_dataPath, "*.shp");
                var layers = shapeFiles.Select(f => new
                {
                    name = Path.GetFileNameWithoutExtension(f),
                    path = f
                });
                
                return Ok(layers);
            }
            catch (Exception ex)
            {
                return BadRequest(new { error = ex.Message });
            }
        }
        
        private Map CreateMap(int width, int height)
        {
            var map = new Map(new Size(width, height));
            map.BackColor = Color.White;
            
            // 添加所有 Shapefile 图层
            var shapeFiles = Directory.GetFiles(_dataPath, "*.shp");
            
            foreach (var shapeFile in shapeFiles)
            {
                var layerName = Path.GetFileNameWithoutExtension(shapeFile);
                var layer = new VectorLayer(layerName);
                layer.DataSource = new ShapeFile(shapeFile, true);
                layer.Style = GetDefaultStyle();
                map.Layers.Add(layer);
            }
            
            return map;
        }
        
        private VectorStyle GetDefaultStyle()
        {
            return new VectorStyle
            {
                Fill = new SolidBrush(Color.FromArgb(150, 144, 238, 144)),
                Outline = new Pen(Color.DarkGreen, 1),
                EnableOutline = true
            };
        }
    }
}

2.5.3 配置 appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "MapData": {
    "Path": "Data"
  }
}

2.6 测试数据准备

2.6.1 下载测试数据

SharpMap Wiki 提供了测试数据下载:

# 下载 states_ugl.zip
wget https://github.com/SharpMap/SharpMap/wiki/resources/states_ugl.zip

# 解压到 Data 目录
unzip states_ugl.zip -d Data/

2.6.2 常用的公开数据源

  1. Natural Earth

  2. OpenStreetMap

  3. GADM

2.6.3 创建测试数据目录结构

项目目录/
├── Data/
│   ├── Vector/
│   │   ├── countries.shp
│   │   ├── states.shp
│   │   └── cities.shp
│   ├── Raster/
│   │   ├── dem.tif
│   │   └── satellite.tif
│   └── Tiles/
│       └── cache/

2.7 项目配置最佳实践

2.7.1 配置文件管理

App.config / appsettings.json 示例

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <!-- 数据路径配置 -->
    <add key="DataPath" value="Data"/>
    <add key="CachePath" value="Cache"/>
    
    <!-- 地图默认设置 -->
    <add key="DefaultSRID" value="4326"/>
    <add key="DefaultMapWidth" value="800"/>
    <add key="DefaultMapHeight" value="600"/>
    
    <!-- 数据库连接 -->
    <add key="PostGISConnection" value="Host=localhost;Database=gis;Username=user;Password=pass"/>
  </appSettings>
</configuration>

2.7.2 依赖注入配置

// Services/MapService.cs
public interface IMapService
{
    Map CreateMap(int width, int height);
    Image RenderMap(Map map);
}

public class MapService : IMapService
{
    private readonly IConfiguration _configuration;
    
    public MapService(IConfiguration configuration)
    {
        _configuration = configuration;
    }
    
    public Map CreateMap(int width, int height)
    {
        var map = new Map(new Size(width, height));
        map.BackColor = Color.White;
        return map;
    }
    
    public Image RenderMap(Map map)
    {
        return map.GetMap();
    }
}

// Program.cs 或 Startup.cs
services.AddSingleton<IMapService, MapService>();

2.7.3 日志配置

using Microsoft.Extensions.Logging;

public class MapController : ControllerBase
{
    private readonly ILogger<MapController> _logger;
    private readonly IMapService _mapService;
    
    public MapController(ILogger<MapController> logger, IMapService mapService)
    {
        _logger = logger;
        _mapService = mapService;
    }
    
    [HttpGet("image")]
    public IActionResult GetMapImage()
    {
        _logger.LogInformation("开始渲染地图...");
        
        try
        {
            var map = _mapService.CreateMap(800, 600);
            // ...
            
            _logger.LogInformation("地图渲染完成");
            return File(/* ... */);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "地图渲染失败");
            return StatusCode(500);
        }
    }
}

2.8 常见问题与解决方案

2.8.1 NuGet 包安装问题

问题:安装 SharpMap 时出现依赖冲突

解决方案

# 清理 NuGet 缓存
dotnet nuget locals all --clear

# 重新安装
Install-Package SharpMap -Force

2.8.2 程序集引用问题

问题:找不到 SharpMap.UI 中的控件

解决方案

  1. 确保已安装 SharpMap.UI 包
  2. 重新构建项目
  3. 手动添加工具箱项

2.8.3 运行时错误

问题:System.Drawing 相关异常

解决方案

// 在 .NET Core/5+ 项目中,需要确保启用了 System.Drawing.Common
// 在 runtimeconfig.json 中添加:
{
  "runtimeOptions": {
    "configProperties": {
      "System.Drawing.EnableUnixSupport": true
    }
  }
}

2.8.4 跨平台问题

问题:在 Linux 上运行 SharpMap

解决方案

# 安装 libgdiplus
# Ubuntu/Debian
sudo apt-get install libgdiplus

# CentOS/RHEL
sudo yum install libgdiplus

# macOS
brew install mono-libgdiplus

2.9 本章小结

本章详细介绍了 SharpMap 开发环境的配置和项目创建:

  1. 环境准备:了解了 .NET 版本选择和开发工具配置
  2. WinForms 项目:创建了基本的 WinForms 地图应用
  3. WPF 项目:学习了如何在 WPF 中使用 SharpMap
  4. 控制台项目:实现了服务端地图渲染
  5. ASP.NET 项目:创建了地图 Web 服务
  6. 测试数据:准备了测试数据和目录结构
  7. 最佳实践:了解了项目配置和依赖注入

2.10 参考资源


下一章预告:第03章将深入讲解 SharpMap 的核心架构和类库设计,包括 Map 类、图层类、数据提供者等核心组件的详细介绍。

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