用于读取 ESRI Shapefile (SHP) 文件并在 Windows Forms 窗口中绘制地理要素。该实现使用 DotSpatial 库处理 Shapefile 文件,支持点、线、面等几何类型的绘制。
解决方案结构
ShapefileViewer/
├── ShapefileViewer.sln
├── ShapefileViewer/
│ ├── App.config
│ ├── Form1.cs
│ ├── Form1.Designer.cs
│ ├── Form1.resx
│ ├── Program.cs
│ ├── ShpReader.cs
│ ├── MapRenderer.cs
│ └── Properties/
│ ├── AssemblyInfo.cs
│ └── Resources.Designer.cs
└── packages.config
完整代码实现
1. 主窗体 (Form1.cs)
csharp
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Windows.Forms;
using DotSpatial.Data;
using DotSpatial.Symbology;
using DotSpatial.Topology;
namespace ShapefileViewer
{
public partial class Form1 : Form
{
private ShpReader shpReader;
private MapRenderer mapRenderer;
private FeatureSet currentLayer;
private ScaleBar scaleBar;
private NorthArrow northArrow;
private bool showScaleBar = true;
private bool showNorthArrow = true;
private bool showGrid = true;
private float zoomFactor = 1.0f;
private PointF panOffset = new PointF(0, 0);
private Point lastMousePosition;
private bool isPanning = false;
public Form1()
{
InitializeComponent();
InitializeMapComponents();
InitializeUI();
}
private void InitializeMapComponents()
{
shpReader = new ShpReader();
mapRenderer = new MapRenderer();
scaleBar = new ScaleBar();
northArrow = new NorthArrow();
}
private void InitializeUI()
{
// 窗体设置
this.Text = "Shapefile 查看器";
this.Size = new Size(1200, 800);
this.BackColor = Color.LightGray;
this.DoubleBuffered = true;
// 创建地图控件
mapPanel = new Panel
{
Location = new Point(10, 10),
Size = new Size(880, 700),
BorderStyle = BorderStyle.FixedSingle,
BackColor = Color.White,
Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right
};
mapPanel.Paint += MapPanel_Paint;
mapPanel.MouseWheel += MapPanel_MouseWheel;
mapPanel.MouseDown += MapPanel_MouseDown;
mapPanel.MouseMove += MapPanel_MouseMove;
mapPanel.MouseUp += MapPanel_MouseUp;
// 创建控制面板
controlPanel = new Panel
{
Location = new Point(900, 10),
Size = new Size(280, 700),
BorderStyle = BorderStyle.FixedSingle,
BackColor = Color.FromArgb(240, 240, 240),
Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right
};
// 添加控件
int yPos = 20;
int labelWidth = 80;
int controlWidth = 170;
int rowHeight = 30;
// 打开文件按钮
btnOpen = new Button
{
Text = "打开 Shapefile",
Location = new Point(20, yPos),
Size = new Size(controlWidth, 30),
BackColor = Color.SteelBlue,
ForeColor = Color.White,
FlatStyle = FlatStyle.Flat
};
btnOpen.FlatAppearance.BorderSize = 0;
btnOpen.Click += BtnOpen_Click;
controlPanel.Controls.Add(btnOpen);
// 缩放控制
yPos += rowHeight + 10;
lblZoom = new Label { Text = "缩放:", Location = new Point(20, yPos), Size = new Size(labelWidth, 20) };
controlPanel.Controls.Add(lblZoom);
tbZoom = new TrackBar
{
Minimum = 10,
Maximum = 500,
Value = 100,
TickFrequency = 10,
Location = new Point(20, yPos + 25),
Size = new Size(controlWidth, 45)
};
tbZoom.ValueChanged += TbZoom_ValueChanged;
controlPanel.Controls.Add(tbZoom);
lblZoomValue = new Label { Text = "100%", Location = new Point(controlWidth + 30, yPos + 40), Size = new Size(50, 20) };
controlPanel.Controls.Add(lblZoomValue);
// 平移控制
yPos += rowHeight + 60;
btnPan = new Button
{
Text = "平移模式",
Location = new Point(20, yPos),
Size = new Size(controlWidth, 30),
BackColor = Color.ForestGreen,
ForeColor = Color.White,
FlatStyle = FlatStyle.Flat
};
btnPan.FlatAppearance.BorderSize = 0;
btnPan.Click += BtnPan_Click;
controlPanel.Controls.Add(btnPan);
btnResetView = new Button
{
Text = "重置视图",
Location = new Point(20, yPos + 40),
Size = new Size(controlWidth, 30),
BackColor = Color.Orange,
ForeColor = Color.White,
FlatStyle = FlatStyle.Flat
};
btnResetView.FlatAppearance.BorderSize = 0;
btnResetView.Click += BtnResetView_Click;
controlPanel.Controls.Add(btnResetView);
// 图层控制
yPos += rowHeight + 90;
chkShowScaleBar = new CheckBox { Text = "显示比例尺", Location = new Point(20, yPos), Checked = true, AutoSize = true };
chkShowScaleBar.CheckedChanged += ChkShowScaleBar_CheckedChanged;
controlPanel.Controls.Add(chkShowScaleBar);
chkShowNorthArrow = new CheckBox { Text = "显示指北针", Location = new Point(20, yPos + 30), Checked = true, AutoSize = true };
chkShowNorthArrow.CheckedChanged += ChkShowNorthArrow_CheckedChanged;
controlPanel.Controls.Add(chkShowNorthArrow);
chkShowGrid = new CheckBox { Text = "显示网格", Location = new Point(20, yPos + 60), Checked = true, AutoSize = true };
chkShowGrid.CheckedChanged += ChkShowGrid_CheckedChanged;
controlPanel.Controls.Add(chkShowGrid);
// 图层列表
yPos += rowHeight + 100;
lblLayers = new Label { Text = "图层:", Location = new Point(20, yPos), AutoSize = true };
controlPanel.Controls.Add(lblLayers);
lstLayers = new ListBox
{
Location = new Point(20, yPos + 25),
Size = new Size(controlWidth, 150),
SelectionMode = SelectionMode.One
};
lstLayers.SelectedIndexChanged += LstLayers_SelectedIndexChanged;
controlPanel.Controls.Add(lstLayers);
// 状态标签
statusLabel = new Label
{
Text = "就绪",
Location = new Point(10, 720),
Size = new Size(1160, 20),
Anchor = AnchorStyles.Bottom | AnchorStyles.Left
};
this.Controls.Add(statusLabel);
// 添加控件到窗体
this.Controls.Add(mapPanel);
this.Controls.Add(controlPanel);
}
#region 控件声明
private Panel mapPanel;
private Panel controlPanel;
private Button btnOpen;
private Button btnPan;
private Button btnResetView;
private TrackBar tbZoom;
private Label lblZoom;
private Label lblZoomValue;
private CheckBox chkShowScaleBar;
private CheckBox chkShowNorthArrow;
private CheckBox chkShowGrid;
private Label lblLayers;
private ListBox lstLayers;
private Label statusLabel;
#endregion
#region 事件处理
private void BtnOpen_Click(object sender, EventArgs e)
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.Filter = "Shapefile 文件 (*.shp)|*.shp|所有文件 (*.*)|*.*";
openFileDialog.Title = "选择 Shapefile 文件";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
try
{
statusLabel.Text = $"正在加载: {Path.GetFileName(openFileDialog.FileName)}...";
Application.DoEvents();
// 加载 shapefile
currentLayer = shpReader.LoadShapefile(openFileDialog.FileName);
if (currentLayer != null)
{
// 添加到图层列表
lstLayers.Items.Add(Path.GetFileNameWithoutExtension(openFileDialog.FileName));
lstLayers.SelectedIndex = lstLayers.Items.Count - 1;
// 重置视图
zoomFactor = 1.0f;
panOffset = new PointF(0, 0);
tbZoom.Value = 100;
lblZoomValue.Text = "100%";
statusLabel.Text = $"已加载: {Path.GetFileName(openFileDialog.FileName)} | 要素数: {currentLayer.Features.Count}";
}
else
{
statusLabel.Text = "加载失败: 无效的文件格式";
}
}
catch (Exception ex)
{
statusLabel.Text = $"错误: {ex.Message}";
MessageBox.Show($"加载 Shapefile 失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
mapPanel.Invalidate();
}
}
}
}
private void MapPanel_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.Clear(Color.White);
if (currentLayer == null) return;
// 应用变换
g.TranslateTransform(panOffset.X, panOffset.Y);
g.ScaleTransform(zoomFactor, zoomFactor);
// 绘制地图内容
mapRenderer.Render(g, currentLayer, mapPanel.ClientSize);
// 绘制网格
if (showGrid)
{
mapRenderer.DrawGrid(g, mapPanel.ClientSize, zoomFactor);
}
// 绘制指北针
if (showNorthArrow)
{
northArrow.Draw(g, new PointF(mapPanel.ClientSize.Width - 50, 50), 30);
}
// 绘制比例尺
if (showScaleBar)
{
scaleBar.Draw(g, new PointF(20, mapPanel.ClientSize.Height - 40), zoomFactor);
}
}
private void TbZoom_ValueChanged(object sender, EventArgs e)
{
zoomFactor = tbZoom.Value / 100.0f;
lblZoomValue.Text = $"{tbZoom.Value}%";
mapPanel.Invalidate();
}
private void BtnPan_Click(object sender, EventArgs e)
{
isPanning = !isPanning;
btnPan.BackColor = isPanning ? Color.OrangeRed : Color.ForestGreen;
mapPanel.Cursor = isPanning ? Cursors.Hand : Cursors.Default;
}
private void BtnResetView_Click(object sender, EventArgs e)
{
zoomFactor = 1.0f;
panOffset = new PointF(0, 0);
tbZoom.Value = 100;
lblZoomValue.Text = "100%";
mapPanel.Invalidate();
}
private void ChkShowScaleBar_CheckedChanged(object sender, EventArgs e)
{
showScaleBar = chkShowScaleBar.Checked;
mapPanel.Invalidate();
}
private void ChkShowNorthArrow_CheckedChanged(object sender, EventArgs e)
{
showNorthArrow = chkShowNorthArrow.Checked;
mapPanel.Invalidate();
}
private void ChkShowGrid_CheckedChanged(object sender, EventArgs e)
{
showGrid = chkShowGrid.Checked;
mapPanel.Invalidate();
}
private void LstLayers_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstLayers.SelectedIndex >= 0)
{
// 在实际应用中,这里应该加载选中的图层
// 本示例中只显示当前加载的图层
mapPanel.Invalidate();
}
}
private void MapPanel_MouseWheel(object sender, MouseEventArgs e)
{
// 缩放功能
int zoomDelta = e.Delta > 0 ? 10 : -10;
tbZoom.Value = Math.Min(Math.Max(tbZoom.Minimum, tbZoom.Value + zoomDelta), tbZoom.Maximum);
}
private void MapPanel_MouseDown(object sender, MouseEventArgs e)
{
if (isPanning && e.Button == MouseButtons.Left)
{
lastMousePosition = e.Location;
mapPanel.Capture = true;
}
}
private void MapPanel_MouseMove(object sender, MouseEventArgs e)
{
if (isPanning && e.Button == MouseButtons.Left)
{
panOffset.X += e.X - lastMousePosition.X;
panOffset.Y += e.Y - lastMousePosition.Y;
lastMousePosition = e.Location;
mapPanel.Invalidate();
}
}
private void MapPanel_MouseUp(object sender, MouseEventArgs e)
{
if (isPanning && e.Button == MouseButtons.Left)
{
mapPanel.Capture = false;
}
}
#endregion
}
#region 辅助类
public class ShpReader
{
public FeatureSet LoadShapefile(string filePath)
{
if (!File.Exists(filePath))
throw new FileNotFoundException("Shapefile 文件不存在", filePath);
// 检查是否存在同名的 .shx 文件
string shxPath = Path.ChangeExtension(filePath, ".shx");
string dbfPath = Path.ChangeExtension(filePath, ".dbf");
if (!File.Exists(shxPath))
throw new FileNotFoundException("找不到关联的 .shx 文件", shxPath);
if (!File.Exists(dbfPath))
throw new FileNotFoundException("找不到关联的 .dbf 文件", dbfPath);
// 使用 DotSpatial 加载 Shapefile
return FeatureSet.Open(filePath);
}
}
public class MapRenderer
{
private readonly Font labelFont = new Font("Arial", 8);
private readonly Brush labelBrush = Brushes.Black;
private readonly Pen gridPen = new Pen(Color.LightGray, 1);
public void Render(Graphics g, FeatureSet featureSet, Size panelSize)
{
if (featureSet == null || featureSet.Features.Count == 0)
return;
// 计算边界框
Extent extent = featureSet.Extent;
double width = extent.Width;
double height = extent.Height;
// 计算缩放比例
float scaleX = (float)(panelSize.Width / width);
float scaleY = (float)(panelSize.Height / height);
float scale = Math.Min(scaleX, scaleY) * 0.9f; // 留出一些边距
// 计算偏移量以居中显示
float offsetX = (float)(-extent.MinX * scale) + (panelSize.Width - (float)width * scale) / 2;
float offsetY = (float)(extent.MaxY * scale) + (panelSize.Height - (float)height * scale) / 2;
// 绘制每个要素
foreach (IFeature feature in featureSet.Features)
{
IGeometry geometry = feature.BasicGeometry;
DrawGeometry(g, geometry, scale, offsetX, offsetY);
}
}
private void DrawGeometry(Graphics g, IGeometry geometry, float scale, float offsetX, float offsetY)
{
if (geometry is Point point)
{
DrawPoint(g, point, scale, offsetX, offsetY);
}
else if (geometry is LineString lineString)
{
DrawLineString(g, lineString, scale, offsetX, offsetY);
}
else if (geometry is Polygon polygon)
{
DrawPolygon(g, polygon, scale, offsetX, offsetY);
}
else if (geometry is MultiPoint multiPoint)
{
foreach (Point pt in multiPoint.Geometries)
{
DrawPoint(g, pt, scale, offsetX, offsetY);
}
}
else if (geometry is MultiLineString multiLineString)
{
foreach (LineString ls in multiLineString.Geometries)
{
DrawLineString(g, ls, scale, offsetX, offsetY);
}
}
else if (geometry is MultiPolygon multiPolygon)
{
foreach (Polygon poly in multiPolygon.Geometries)
{
DrawPolygon(g, poly, scale, offsetX, offsetY);
}
}
}
private void DrawPoint(Graphics g, Point point, float scale, float offsetX, float offsetY)
{
float x = (float)(point.X * scale + offsetX);
float y = (float)(-point.Y * scale + offsetY); // Y坐标反转
// 绘制点符号
g.FillEllipse(Brushes.Red, x - 3, y - 3, 6, 6);
g.DrawEllipse(Pens.Black, x - 3, y - 3, 6, 6);
}
private void DrawLineString(Graphics g, LineString lineString, float scale, float offsetX, float offsetY)
{
PointF[] points = new PointF[lineString.Coordinates.Count];
for (int i = 0; i < lineString.Coordinates.Count; i++)
{
Coordinate coord = lineString.Coordinates[i];
float x = (float)(coord.X * scale + offsetX);
float y = (float)(-coord.Y * scale + offsetY); // Y坐标反转
points[i] = new PointF(x, y);
}
// 绘制线
g.DrawLines(Pens.Blue, points);
}
private void DrawPolygon(Graphics g, Polygon polygon, float scale, float offsetX, float offsetY)
{
// 绘制外环
DrawRing(g, polygon.Shell, scale, offsetX, offsetY, Pens.Green, Brushes.LightGreen);
// 绘制内环(孔洞)
foreach (LinearRing hole in polygon.Holes)
{
DrawRing(g, hole, scale, offsetX, offsetY, Pens.Red, Brushes.LightPink);
}
}
private void DrawRing(Graphics g, LinearRing ring, float scale, float offsetX, float offsetY, Pen pen, Brush brush)
{
PointF[] points = new PointF[ring.Coordinates.Count];
for (int i = 0; i < ring.Coordinates.Count; i++)
{
Coordinate coord = ring.Coordinates[i];
float x = (float)(coord.X * scale + offsetX);
float y = (float)(-coord.Y * scale + offsetY); // Y坐标反转
points[i] = new PointF(x, y);
}
// 填充多边形
if (brush != null)
g.FillPolygon(brush, points);
// 绘制边界
g.DrawPolygon(pen, points);
}
public void DrawGrid(Graphics g, Size panelSize, float zoomFactor)
{
float gridSize = 50 * zoomFactor;
float offsetX = panelSize.Width % gridSize / 2;
float offsetY = panelSize.Height % gridSize / 2;
for (float x = offsetX; x < panelSize.Width; x += gridSize)
{
g.DrawLine(gridPen, x, 0, x, panelSize.Height);
}
for (float y = offsetY; y < panelSize.Height; y += gridSize)
{
g.DrawLine(gridPen, 0, y, panelSize.Width, y);
}
}
}
public class ScaleBar
{
public void Draw(Graphics g, PointF location, float zoomFactor)
{
float barLength = 100 * zoomFactor;
float barHeight = 10;
// 绘制比例尺条
g.FillRectangle(Brushes.White, location.X, location.Y, barLength, barHeight);
g.DrawRectangle(Pens.Black, location.X, location.Y, barLength, barHeight);
// 绘制刻度
g.DrawLine(Pens.Black, location.X, location.Y - 5, location.X, location.Y + barHeight + 5);
g.DrawLine(Pens.Black, location.X + barLength, location.Y - 5, location.X + barLength, location.Y + barHeight + 5);
// 绘制标签
Font font = new Font("Arial", 8);
string label = $"{barLength / 100 * 10} km"; // 简化计算
g.DrawString(label, font, Brushes.Black, location.X, location.Y + barHeight + 5);
}
}
public class NorthArrow
{
public void Draw(Graphics g, PointF location, float size)
{
// 绘制箭头
PointF[] arrowPoints =
{
new PointF(location.X, location.Y - size / 2),
new PointF(location.X + size / 2, location.Y + size / 2),
new PointF(location.X - size / 2, location.Y + size / 2)
};
g.FillPolygon(Brushes.White, arrowPoints);
g.DrawPolygon(Pens.Black, arrowPoints);
// 绘制文字
Font font = new Font("Arial", 10, FontStyle.Bold);
g.DrawString("N", font, Brushes.Black, location.X - 5, location.Y - size / 2 - 15);
}
}
#endregion
}
2. 程序入口 (Program.cs)
csharp
using System;
using System.Windows.Forms;
namespace ShapefileViewer
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
3. 应用程序配置文件 (App.config)
xml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
</startup>
</configuration>
4. NuGet 包配置 (packages.config)
xml
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DotSpatial.Core" version="1.9.0" targetFramework="net472" />
<package id="DotSpatial.Data" version="1.9.0" targetFramework="net472" />
<package id="DotSpatial.Symbology" version="1.9.0" targetFramework="net472" />
<package id="DotSpatial.Topology" version="1.9.0" targetFramework="net472" />
<package id="NetTopologySuite" version="1.15.3" targetFramework="net472" />
</packages>
功能特点
1. 文件读取功能
- 支持标准 ESRI Shapefile 格式 (.shp)
- 自动检测并加载关联的 .shx 和 .dbf 文件
- 支持点、线、面等几何类型
- 支持多点、多线、多面体等复合类型
2. 地图显示功能
- 自动缩放以适应窗口大小
- 平滑的缩放和平移操作
- 支持鼠标滚轮缩放
- 支持拖拽平移
- 网格显示(可开关)
3. 地图元素
- 比例尺(可开关)
- 指北针(可开关)
- 图层管理
- 状态显示
4. 用户界面
- 直观的控制面板
- 缩放滑块控制
- 平移模式切换
- 视图重置按钮
- 图层列表
- 状态栏
使用说明
1. 环境要求
-
.NET Framework 4.7.2 或更高版本
-
安装 NuGet 包:
Install-Package DotSpatial.Core Install-Package DotSpatial.Data Install-Package DotSpatial.Symbology Install-Package DotSpatial.Topology Install-Package NetTopologySuite
2. 操作步骤
- 运行程序
- 点击"打开 Shapefile"按钮
- 选择要打开的 .shp 文件
- 使用鼠标滚轮缩放地图
- 点击"平移模式"后拖拽地图
- 使用控制面板调整显示选项
3. 支持的 Shapefile 类型
- 点 (Point)
- 线 (Polyline)
- 面 (Polygon)
- 多点 (MultiPoint)
- 多线 (MultiPolyline)
- 多面体 (MultiPolygon)
参考代码 学会用C#文件读取的shp(shapefile格式)文件并在窗口绘制 www.youwenfan.com/contentcst/39627.html
技术实现细节
1. Shapefile 读取
csharp
public FeatureSet LoadShapefile(string filePath)
{
// 检查文件存在性
if (!File.Exists(filePath)) throw new FileNotFoundException(...);
// 检查关联文件
string shxPath = Path.ChangeExtension(filePath, ".shx");
string dbfPath = Path.ChangeExtension(filePath, ".dbf");
if (!File.Exists(shxPath) || !File.Exists(dbfPath))
throw new FileNotFoundException(...);
// 使用 DotSpatial 加载
return FeatureSet.Open(filePath);
}
2. 坐标变换
csharp
// 计算缩放比例
float scaleX = (float)(panelSize.Width / width);
float scaleY = (float)(panelSize.Height / height);
float scale = Math.Min(scaleX, scaleY) * 0.9f;
// 计算偏移量
float offsetX = (float)(-extent.MinX * scale) + (panelSize.Width - (float)width * scale) / 2;
float offsetY = (float)(extent.MaxY * scale) + (panelSize.Height - (float)height * scale) / 2;
// 应用变换
float x = (float)(coord.X * scale + offsetX);
float y = (float)(-coord.Y * scale + offsetY); // Y坐标反转
3. 几何绘制
csharp
// 点绘制
g.FillEllipse(Brushes.Red, x - 3, y - 3, 6, 6);
g.DrawEllipse(Pens.Black, x - 3, y - 3, 6, 6);
// 线绘制
g.DrawLines(Pens.Blue, points);
// 面绘制
g.FillPolygon(brush, points);
g.DrawPolygon(pen, points);
扩展功能建议
1. 添加属性查询功能
csharp
// 在 Form1 类中添加
private void LstLayers_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (lstLayers.SelectedIndex >= 0 && currentLayer != null)
{
// 显示属性表
ShowAttributeTable(currentLayer);
}
}
private void ShowAttributeTable(FeatureSet layer)
{
Form attrForm = new Form();
attrForm.Text = "属性表 - " + layer.Name;
attrForm.Size = new Size(800, 600);
DataGridView grid = new DataGridView();
grid.Dock = DockStyle.Fill;
grid.ReadOnly = true;
grid.DataSource = layer.DataTable;
attrForm.Controls.Add(grid);
attrForm.ShowDialog();
}
2. 添加空间查询功能
csharp
// 在 MapRenderer 类中添加
public IFeature SelectFeature(PointF clickPoint, FeatureSet layer, float scale, float offsetX, float offsetY)
{
// 将屏幕坐标转换为地图坐标
double mapX = (clickPoint.X - offsetX) / scale;
double mapY = -(clickPoint.Y - offsetY) / scale;
// 查找最近的要素
IFeature closestFeature = null;
double minDistance = double.MaxValue;
foreach (IFeature feature in layer.Features)
{
double distance = feature.Geometry.Distance(new Coordinate(mapX, mapY));
if (distance < minDistance)
{
minDistance = distance;
closestFeature = feature;
}
}
// 检查是否在容差范围内
if (minDistance < 10.0 / scale) // 10像素容差
return closestFeature;
return null;
}
3. 添加图层样式编辑
csharp
// 在 Form1 类中添加
private void LstLayers_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right && lstLayers.SelectedIndex >= 0)
{
ContextMenuStrip menu = new ContextMenuStrip();
ToolStripMenuItem colorItem = new ToolStripMenuItem("更改颜色");
colorItem.Click += (s, args) => ChangeLayerColor();
menu.Items.Add(colorItem);
ToolStripMenuItem widthItem = new ToolStripMenuItem("更改线宽");
widthItem.Click += (s, args) => ChangeLineWidth();
menu.Items.Add(widthItem);
menu.Show(lstLayers, e.Location);
}
}
private void ChangeLayerColor()
{
if (currentLayer != null)
{
ColorDialog dialog = new ColorDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
// 在实际应用中,更新图层样式
mapPanel.Invalidate();
}
}
}
4. 添加导出图片功能
csharp
// 在 Form1 类中添加
private void BtnExport_Click(object sender, EventArgs e)
{
using (SaveFileDialog saveDialog = new SaveFileDialog())
{
saveDialog.Filter = "PNG 图片 (*.png)|*.png|JPEG 图片 (*.jpg)|*.jpg|BMP 图片 (*.bmp)|*.bmp";
saveDialog.Title = "导出地图";
if (saveDialog.ShowDialog() == DialogResult.OK)
{
Bitmap bmp = new Bitmap(mapPanel.Width, mapPanel.Height);
mapPanel.DrawToBitmap(bmp, new Rectangle(0, 0, mapPanel.Width, mapPanel.Height));
bmp.Save(saveDialog.FileName);
statusLabel.Text = $"地图已导出到: {saveDialog.FileName}";
}
}
}
常见问题解决
1. 文件加载失败
- 问题:找不到 .shx 或 .dbf 文件
- 解决:确保 Shapefile 的三个文件 (.shp, .shx, .dbf) 在同一目录下
2. 中文乱码
-
问题:属性表中的中文显示为乱码
-
解决:设置正确的编码格式
csharp// 在 ShpReader 类中添加 featureSet.Encoding = Encoding.GetEncoding("GB2312"); // 或 UTF-8
3. 性能问题
- 问题:大型 Shapefile 加载缓慢
- 解决 :
- 使用空间索引
- 实现要素简化
- 分块加载
项目总结
这个 C# Shapefile 查看器提供了完整的地理数据可视化解决方案,具有以下特点:
-
完整的 Shapefile 支持:
- 读取和解析 SHP、SHX、DBF 文件
- 支持所有标准几何类型
- 处理属性数据
-
丰富的地图功能:
- 平滑的缩放和平移
- 多种地图元素(比例尺、指北针、网格)
- 图层管理
-
用户友好界面:
- 直观的控制面板
- 状态显示
- 响应式设计
-
可扩展架构:
- 模块化设计
- 易于添加新功能
- 支持自定义渲染