C# 讀取和繪制 Shapefile (SHP) 文件的詳細過程
用于讀取 ESRI Shapefile (SHP) 文件并在 Windows Forms 窗口中繪制地理要素。該實現(xiàn)使用 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
完整代碼實現(xiàn)
1. 主窗體 (Form1.cs)
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;
// 創(chuàng)建地圖控件
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;
// 創(chuàng)建控制面板
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 = "顯示網(wǎng)格", 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);
// 狀態(tài)標簽
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)} | 要素數(shù): {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);
// 繪制地圖內(nèi)容
mapRenderer.Render(g, currentLayer, mapPanel.ClientSize);
// 繪制網(wǎng)格
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("找不到關聯(lián)的 .shx 文件", shxPath);
if (!File.Exists(dbfPath))
throw new FileNotFoundException("找不到關聯(lián)的 .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)
{
// 繪制外環(huán)
DrawRing(g, polygon.Shell, scale, offsetX, offsetY, Pens.Green, Brushes.LightGreen);
// 繪制內(nèi)環(huán)(孔洞)
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)
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 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 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)
- 自動檢測并加載關聯(lián)的 .shx 和 .dbf 文件
- 支持點、線、面等幾何類型
- 支持多點、多線、多面體等復合類型
2. 地圖顯示功能
- 自動縮放以適應窗口大小
- 平滑的縮放和平移操作
- 支持鼠標滾輪縮放
- 支持拖拽平移
- 網(wǎng)格顯示(可開關)
3. 地圖元素
- 比例尺(可開關)
- 指北針(可開關)
- 圖層管理
- 狀態(tài)顯示
4. 用戶界面
- 直觀的控制面板
- 縮放滑塊控制
- 平移模式切換
- 視圖重置按鈕
- 圖層列表
- 狀態(tài)欄
使用說明
1. 環(huán)境要求
.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
技術實現(xiàn)細節(jié)
1. Shapefile 讀取
public FeatureSet LoadShapefile(string filePath)
{
// 檢查文件存在性
if (!File.Exists(filePath)) throw new FileNotFoundException(...);
// 檢查關聯(lián)文件
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. 坐標變換
// 計算縮放比例 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. 幾何繪制
// 點繪制 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. 添加屬性查詢功能
// 在 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. 添加空間查詢功能
// 在 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;
}
}
// 檢查是否在容差范圍內(nèi)
if (minDistance < 10.0 / scale) // 10像素容差
return closestFeature;
return null;
}3. 添加圖層樣式編輯
// 在 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. 添加導出圖片功能
// 在 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. 中文亂碼
- 問題:屬性表中的中文顯示為亂碼
- 解決:設置正確的編碼格式
// 在 ShpReader 類中添加
featureSet.Encoding = Encoding.GetEncoding("GB2312"); // 或 UTF-8
3. 性能問題
- 問題:大型 Shapefile 加載緩慢
- 解決:
- 使用空間索引
- 實現(xiàn)要素簡化
- 分塊加載
項目總結
這個 C# Shapefile 查看器提供了完整的地理數(shù)據(jù)可視化解決方案,具有以下特點:
- 完整的 Shapefile 支持:
- 讀取和解析 SHP、SHX、DBF 文件
- 支持所有標準幾何類型
- 處理屬性數(shù)據(jù)
- 豐富的地圖功能:
- 平滑的縮放和平移
- 多種地圖元素(比例尺、指北針、網(wǎng)格)
- 圖層管理
- 用戶友好界面:
- 直觀的控制面板
- 狀態(tài)顯示
- 響應式設計
- 可擴展架構:
- 模塊化設計
- 易于添加新功能
- 支持自定義渲染
到此這篇關于C# 讀取和繪制 Shapefile (SHP) 文件的詳細過程的文章就介紹到這了,更多相關C# 讀取 Shapefile文件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C# 指針內(nèi)存控制Marshal內(nèi)存數(shù)據(jù)存儲原理分析
這篇文章主要介紹了C# 指針 內(nèi)存控制 Marshal 內(nèi)存數(shù)據(jù)存儲原理分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02
c#使用微信接口開發(fā)微信門戶應用中微信消息的處理和應答
這篇文章主要介紹了c#使用微信接口開發(fā)微信門戶中的微信消息的處理和應答的過程,需要的朋友可以參考下2014-03-03
C#從windows剪貼板獲取并顯示文本內(nèi)容的方法
這篇文章主要介紹了C#從windows剪貼板獲取并顯示文本內(nèi)容的方法,涉及C#操作剪貼板的相關技巧,非常具有實用價值,需要的朋友可以參考下2015-04-04
如何利用C#通過sql語句操作Sqlserver數(shù)據(jù)庫教程
ado.net提供了豐富的數(shù)據(jù)庫操作,下面這篇文章主要給大家介紹了關于如何利用C#通過sql語句操作Sqlserver數(shù)據(jù)庫教程的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-10-10
C#實現(xiàn)數(shù)字字符串左補零的六種方式技巧
在編程旅程中,常常會遇到需要將數(shù)字字符串左補齊 0 的情況,這種格式化需求在實際開發(fā)中相當普遍,在 C# 中,實現(xiàn)數(shù)字字符串左補齊 0 主要有這 6 種方法,我們一起來看看吧,需要的朋友可以參考下2024-12-12
C# Dynamic之:ExpandoObject,DynamicObject,DynamicMetaOb的應用(下)
本篇文章是對C#中ExpandoObject,DynamicObject,DynamicMetaOb的應用進行了詳細的分析介紹,需要的朋友參考下2013-05-05

